Stringr Introduction from http://blog.fens.me/r-stringr/.

Reference: http://blog.fens.me/r-stringr/

前言

用R语言处理字符串,总觉得很麻烦,即不能用向量的方法进行分割,也不能用循环遍历索引。grep()家族函数常常记不住,paste()函数默认以空格分割,各种不顺手啊!随着使用R语言的场景越来越多,字符串处理是必不可少的。给大家推荐一个由 Hadley Wickham 开发的一个灵活的字符串处理包stringr。

目录

stringr介绍

stringr安装

stringr的API介绍

1. stringr介绍

stringr包被定义为一致的、简单易用的字符串工具集。所有的函数和参数定义都具有一致性,比如,用相同的方法进行NA处理和0长度的向量处理。

字符串处理虽然不是R语言中最主要的功能,却也是必不可少的,数据清洗、可视化等的操作都会用到。对于R语言本身的base包提供的字符串基础函数,随着时间的积累,已经变得很多地方不一致,不规范的命名,不标准的参数定义,很难看一眼就上手使用。字符串处理在其他语言中都是非常方便的事情,R语言在这方面确实落后了。stringr包就是为了解决这个问题,让字符串处理变得简单易用,提供友好的字符串操作接口。

stringr的项目主页:https://cran.r-project.org/web/packages/stringr/index.html

2. stringr安装

本文所使用的系统环境

Win10 64bit R: 3.2.3 x86_64-w64-mingw32/x64 b4bit

stringr是在CRAN发布的标准库,安装起来非常简单,2条命令就可以了。

# ~ R
# Load stringr package
if(!suppressWarnings(require("stringr")))
{
    install.packages("stringr")
    require("stringr")
}

# ~ R
# install.packages('stringr')
# library(stringr)

3. stringr的API介绍

stringr包1.0.0版本,一共提供了30个函数,方便我们对字符串处理。常用的字符串的处理以str_开头来命名,方便更直观理解函数的定义。我们可以根据使用习惯对函数进行分类:

3.1 字符串拼接函数

  • str_c: 字符串拼接。
  • str_join: 字符串拼接,同str_c
  • str_trim: 去掉字符串的空格和TAB()
  • str_pad: 补充字符串的长度
  • str_dup: 复制字符串
  • str_wrap: 控制字符串输出格式
  • str_sub: 截取字符串
  • str_sub: 截取字符串,并赋值,同str_sub

3.1.1 str_c,字符串拼接操作,与str_join完全相同,与paste()行为不完全一致。

函数定义-Description & Usage

str_c(..., sep = "", collapse = NULL) str_join(..., sep = "", collapse = NULL)

参数列表-Arguments

  • : 多参数的输入
  • sep: 把多个字符串拼接为一个大的字符串,用于字符串的分割符。
  • collapse: 把多个向量参数拼接为一个大的字符串,用于字符串的分割符。

把多个字符串拼接为一个大的字符串。Join multiple strings into a single string.


str_c('a','b')
# [1] "ab"
str_c('a','b',sep='-')
# [1] "a-b"
str_c(c('a','a1'),c('b','b1'),sep='-')
# [1] "a-b"   "a1-b1"

# More Examples
str_c("Letter: ", letters)
# [1] "Letter: a" "Letter: b" "Letter: c" "Letter: d"
#  [5] "Letter: e" "Letter: f" "Letter: g" "Letter: h"
#  [9] "Letter: i" "Letter: j" "Letter: k" "Letter: l"
# [13] "Letter: m" "Letter: n" "Letter: o" "Letter: p"
# [17] "Letter: q" "Letter: r" "Letter: s" "Letter: t"
# [21] "Letter: u" "Letter: v" "Letter: w" "Letter: x"
# [25] "Letter: y" "Letter: z"

str_c("Letter", letters, sep = ": ")
# [1] "Letter: a" "Letter: b" "Letter: c" "Letter: d"
#  [5] "Letter: e" "Letter: f" "Letter: g" "Letter: h"
#  [9] "Letter: i" "Letter: j" "Letter: k" "Letter: l"
# [13] "Letter: m" "Letter: n" "Letter: o" "Letter: p"
# [17] "Letter: q" "Letter: r" "Letter: s" "Letter: t"
# [21] "Letter: u" "Letter: v" "Letter: w" "Letter: x"
# [25] "Letter: y" "Letter: z"

str_c(letters, " is for", "...")
# [1] "a is for..." "b is for..." "c is for..."
#  [4] "d is for..." "e is for..." "f is for..."
#  [7] "g is for..." "h is for..." "i is for..."
# [10] "j is for..." "k is for..." "l is for..."
# [13] "m is for..." "n is for..." "o is for..."
# [16] "p is for..." "q is for..." "r is for..."
# [19] "s is for..." "t is for..." "u is for..."
# [22] "v is for..." "w is for..." "x is for..."
# [25] "y is for..." "z is for..."

str_c(letters[-26], " comes before ", letters[-1])
#  [1] "a comes before b" "b comes before c"
#  [3] "c comes before d" "d comes before e"
#  [5] "e comes before f" "f comes before g"
#  [7] "g comes before h" "h comes before i"
#  [9] "i comes before j" "j comes before k"
# [11] "k comes before l" "l comes before m"
# [13] "m comes before n" "n comes before o"
# [15] "o comes before p" "p comes before q"
# [17] "q comes before r" "r comes before s"
# [19] "s comes before t" "t comes before u"
# [21] "u comes before v" "v comes before w"
# [23] "w comes before x" "x comes before y"
# [25] "y comes before z"

把多个向量参数拼接为一个大的字符串。


str_c(head(letters), collapse = "")
# [1] "abcdef"
str_c(head(letters), collapse = ", ")
# [1] "a, b, c, d, e, f"

# collapse参数,对多个字符串无效
str_c('a','b',collapse = "-")   
# [1] "ab"
str_c(c('a','a1'),c('b','b1'),collapse='-')
# [1] "ab-a1b1"

拼接有NA值的字符串向量时,NA还是NA

# Missing inputs give missing outputs
str_c(c("a", NA, "b"), "-d")
# [1] "a-d" NA    "b-d"

# Use str_replace_NA to display literal NAs:
str_c(str_replace_na(c("a", NA, "b")), "-d")
# [1] "a-d"  "NA-d" "b-d" 

对比str_c()函数和paste()函数之间的不同点。

# 多字符串拼接,默认的sep参数行为不一致
str_c('a','b')
# [1] "ab"
paste('a','b')
# [1] "a b"

# 向量拼接字符串,collapse参数的行为一致
str_c(head(letters), collapse = "")
# [1] "abcdef"
paste(head(letters), collapse = "")
# [1] "abcdef"
 
#拼接有NA值的字符串向量,对NA的处理行为不一致
str_c(c("a", NA, "b"), "-d")
# [1] "a-d" NA    "b-d"
paste(c("a", NA, "b"), "-d")
# [1] "a -d"  "NA -d" "b -d" 

3.1.2 str_trim:去掉字符串的空格和TAB()

函数定义-Description & Usage

str_trim(string, side = c("both", "left", "right"))

参数列表-Arguments

  • string: 字符串,字符串向量。
  • side: 过滤方式,both两边都过滤,left左边过滤,right右边过滤

去掉字符串的空格和TAB(),Trim whitespace from start and end of string.

#只过滤左边的空格
str_trim("  left space\t\n",side='left') 
# [1] "left space\t\n"

#只过滤右边的空格
str_trim("  left space\t\n",side='right')
# [1] "  left space"

#过滤两边的空格
str_trim("  left space\t\n",side='both')
# [1] "left space"

#过滤两边的空格,default
str_trim("\nno space\n\t")
# [1] "no space"

# More Examples
cat("  String with trailing and leading white space\t")
#  String with trailing and leading white space 
str_trim("  String with trailing and leading white space\t")
# [1] "String with trailing and leading white space"

cat("\n\nString with trailing and leading white space    \n\n")
# 
# 
# String with trailing and leading white space    

str_trim("\n\nString with trailing and leading white space    \n\n")
# [1] "String with trailing and leading white space"

3.1.3 str_pad:补充字符串的长度

函数定义-Description & Usage

str_pad(string, width, side = c("left", "right", "both"), pad = " ")

参数列表-Arguments

  • string: 字符串,字符串向量。
  • width: 字符串填充后的长度
  • side: 填充方向,both两边都填充,left左边填充,right右边填充
  • pad: 用于填充的字符

补充字符串的长度。Pad a string

# 从左边补充空格,直到字符串长度为20
str_pad("Stone_Hou", 20, "left")
# [1] "           Stone_Hou"

# 从右边补充空格,直到字符串长度为20
str_pad("Stone_Hou", 20, "right")
# [1] "Stone_Hou           "

# 从左右两边各补充空格,直到字符串长度为20
str_pad("Stone_Hou", 20, "both")
# [1] "     Stone_Hou      "

# 从左右两边各补充x字符,直到字符串长度为20
str_pad("Stone_Hou", 20, "both",'z')
# [1] "zzzzzStone_Houzzzzzz"

# More Examples
rbind(
  str_pad("hadley", 30, "left"),
  str_pad("hadley", 30, "right"),
  str_pad("hadley", 30, "both")
)
#      [,1]                            
# [1,] "                        hadley"
# [2,] "hadley                        "
# [3,] "            hadley            "

# All arguments are vectorised except side
str_pad(c("a", "abc", "abcdef"), 10)
# [1] "         a" "       abc" "    abcdef"
str_pad("a", c(5, 10, 20))
# [1] "    a"                "         a"          
# [3] "                   a"
str_pad("a", 10, pad = c("-", "_", " "))
# [1] "---------a" "_________a" "         a"

# Longer strings than width are returned unchanged
str_pad("hadley", 3)
# [1] "hadley"

3.1.4 str_dup: 复制字符串

函数定义-Description & Usage

str_dup(string, times)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • times: 复制数量

复制一个字符串向量。Duplicate and concatenate strings within a character vector

val <- c("abca4", 123, "cba2")

# 复制2次
str_dup(val, 2)
# [1] "abca4abca4" "123123"     "cba2cba2"  

# 按位置复制
str_dup(val, 1:3)
# [1] "abca4"        "123123"       "cba2cba2cba2"

# More Examples
fruit <- c("apple", "pear", "banana");fruit
# [1] "apple"  "pear"   "banana"
str_dup(fruit, 2)
# [1] "appleapple"   "pearpear"     "bananabanana"
str_dup(fruit, 1:3)
# [1] "apple"              "pearpear"          
# [3] "bananabananabanana"
str_c("ba", str_dup("na", 0:5))
# [1] "ba"           "bana"         "banana"      
# [4] "bananana"     "banananana"   "bananananana"

3.1.5 str_wrap,控制字符串输出格式

函数定义-Description & Usage

str_wrap(string, width = 80, indent = 0, exdent = 0)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • width: 设置一行所占的宽度。
  • indent: 段落首行的缩进值
  • exdent: 段落非首行的缩进值
txt <- 'R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。'

# 设置宽度为40个字符
cat(str_wrap(txt, width = 40), "\n")
# R语言作为统计学一门语言,一直在小众领域
# 闪耀着光芒。直到大数据的爆发,R语言变成
# 了一门炙手可热的数据分析的利器。随着越来
# 越多的工程背景的人的加入,R语言的社区在
# 迅速扩大成长。现在已不仅仅是统计领域,教
# 育,银行,电商,互联网….都在使用R语言。 

# 设置宽度为60字符,首行缩进2字符
cat(str_wrap(txt, width = 60, indent = 2), "\n")
#   R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数
# 据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来
# 越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已
# 不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。 

# 设置宽度为10字符,非首行缩进4字符
cat(str_wrap(txt, width = 10, exdent = 4), "\n")
# R语言作为
#     统计学一
#     门语言,
#     一直在小
#     众领域闪
#     耀着光芒。
#     直到大数据
#     的爆发,R
#     语言变成了
#     一门炙手可
#     热的数据分
#     析的利器。
#     随着越来
#     越多的工程
#     背景的人的
#     加入,R语
#     言的社区在
#     迅速扩大成
#     长。现在已
#     不仅仅是统
#     计领域,教
#     育,银行,
#     电商,互联
#     网….都在使
#     用R语言。

# More Examples
thanks_path <- file.path(R.home("doc"), "THANKS")
thanks <- str_c(readLines(thanks_path), collapse = "\n")
thanks <- word(thanks, 1, 3, fixed("\n\n"))
cat(str_wrap(thanks), "\n")
cat(str_wrap(thanks, width = 40), "\n")
cat(str_wrap(thanks, width = 60, indent = 2), "\n")
cat(str_wrap(thanks, width = 60, exdent = 2), "\n")
# one word one row
cat(str_wrap(thanks, width = 0, exdent = 2), "\n")

3.1.6 str_sub,截取字符串

函数定义-Description & Usage

str_sub(string, start = 1L, end = -1L)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • start : 开始位置
  • end : 结束位置

截取字符串。

txt <- "I am Conan."

# 截取1-4的索引位置的字符串
str_sub(txt, 1, 4)
# [1] "I am"

# 截取1-6的索引位置的字符串
str_sub(txt, end=6)
# [1] "I am C"

# 截取6到结束的索引位置的字符串,default to end length(txt)
str_sub(txt, 6)
# [1] "Conan."

# 分2段截取字符串
str_sub(txt, c(1, 4), c(6, 8))
# [1] "I am C" "m Con" 

# 通过负坐标截取字符串 Negative indices
str_sub(txt, -3)
# [1] "an."
str_sub(txt, end = -3)
# [1] "I am Cona"

# More Example From str_sub help
hw <- "Hadley Wickham"

str_sub(hw, 1, 6)
# [1] "Hadley"
str_sub(hw, end = 6)
# [1] "Hadley"
str_sub(hw, 8, 14)
# [1] "Wickham"
str_sub(hw, 8)
# [1] "Wickham"
str_sub(hw, c(1, 8), c(6, 14))
# str_sub(hw, c(1, 8), c(6, 14))

# Negative indices
str_sub(hw, -1)
# [1] "m"
str_sub(hw, -7)
# [1] "Wickham"
str_sub(hw, end = -7)
# [1] "Hadley W"

# Alternatively, you can pass in a two colum matrix, as in the
# output from str_locate_all
pos <- str_locate_all(hw, "[aeio]")[[1]]
str_sub(hw, pos)
str_sub(hw, pos[, 1], pos[, 2])

# Vectorisation
str_sub(hw, seq_len(str_length(hw)))
#  [1] "Hadley Wickham" "adley Wickham" 
#  [3] "dley Wickham"   "ley Wickham"   
#  [5] "ey Wickham"     "y Wickham"     
#  [7] " Wickham"       "Wickham"       
#  [9] "ickham"         "ckham"         
# [11] "kham"           "ham"           
# [13] "am"             "m" 
str_sub(hw, end = seq_len(str_length(hw)))
#  [1] "H"              "Ha"            
#  [3] "Had"            "Hadl"          
#  [5] "Hadle"          "Hadley"        
#  [7] "Hadley "        "Hadley W"      
#  [9] "Hadley Wi"      "Hadley Wic"    
# [11] "Hadley Wick"    "Hadley Wickh"  
# [13] "Hadley Wickha"  "Hadley Wickham"

# Replacement form
x <- "BBCDEF"
str_sub(x, 1, 1) <- "A"; x
# [1] "ABCDEF"
str_sub(x, -1, -1) <- "K"; x
# [1] "ABCDEK"

# replace E in ABCDEK with GHIJ
str_sub(x, -2, -2) <- "GHIJ"; x
# [1] "ABCDGHIJK"

# replace BCDGHIJ in ABCDGHIJK with non
str_sub(x, 2, -2) <- ""; x

对截取的字符串进行赋值。Extract and replace substrings from a character vector.

x <- "AAABBBCCC"

# 在字符串的1的位置赋值为1
str_sub(x, 1, 1) <- 1; x
# [1] "1AABBBCCC"

# 在字符串从2到-2的位置赋值为2345
str_sub(x, 2, -2) <- "2345"; x
# [1] "12345C"

3.2 字符串计算函数

  • str_count: 字符串计数
  • str_length: 字符串长度
  • str_sort: 字符串值排序
  • str_order: 字符串索引排序,规则同str_sort

3.2.1 str_count, 字符串计数

函数定义-Description & Usage

str_count(string, pattern = "")

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配的字符。

对字符串中匹配的字符计数

# Count the number of matches in a string.
str_count('aaa444sssddd', "a")
# [1] 3

对字符串向量中匹配的字符计数

fruit <- c("apple", "banana", "pear", "pineapple")
str_count(fruit, "a")
# [1] 1 3 1 1
str_count(fruit, "p")
# [1] 2 0 1 3
str_count(fruit, "e")
# [1] 1 0 1 2

# matches 1-1
str_count(fruit, c("a", "b", "p", "p"))
# [1] 1 1 1 3

对字符串中的’.’字符计数,由于.是正则表达式的匹配符,直接判断计数的结果是不对的。

str_count(c("a.", ".", ".a.",NA), ".")
# [1]  2  1  3 NA

# 用fixed匹配字符
str_count(c("a.", ".", ".a.",NA), fixed("."))
# [1]  1  1  2 NA

# 用\\匹配字符
str_count(c("a.", ".", ".a.",NA), "\\.")
# [1]  1  1  2 NA

3.2.2 str_length,字符串长度

函数定义-Description & Usage

str_length(string)

参数列表-Arguments

  • string: 字符串,字符串向量。

计算字符串的长度,The length of a string :

str_length(c("I", "am", "张丹", NA))
# [1]  1  2  2 NA

# More Examples

str_length(letters)
# [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# [26] 1
str_length(NA)
# [1] NA
str_length(factor("abc"))
# [1] 3
str_length(c("i", "like", "programming", NA))
# [1]  1  4 11 NA

# Two ways of representing a u with an umlaut
u1 <- "\u00fc"
u2 <- stringi::stri_trans_nfd(u1)
# The print the same:
u1
u2
# But have a different length
str_length(u1)
# [1] 1
str_length(u2)
# [1] 2

# Even though they have the same number of characters
str_count(u1)
# [1] 1
str_count(u2)
# [1] 1

3.2.3 str_sort, 字符串值排序,同str_order索引排序

函数定义-Description & Usage

str_sort(x, decreasing = FALSE, na_last = TRUE, locale = "", ...)

str_order(x, decreasing = FALSE, na_last = TRUE, locale = "", ...)

参数列表-Arguments

  • x: 字符串,字符串向量。
  • decreasing: 排序方向。
  • na_last:NA值的存放位置,一共3个值,TRUE放到最后,FALSE放到最前,NA过滤处理
  • locale:按哪种语言习惯排序

对字符串值进行排序。Order or sort a character vector.

# 按ASCII字母排序
str_sort(c('a',1,2,'11'), locale = "en")  
# [1] "1"  "11" "2"  "a" 

# 倒序排序
str_sort(letters,decreasing=TRUE)
#  [1] "z" "y" "x" "w" "v" "u" "t" "s" "r" "q" "p" "o" "n" "m" "l" "k" "j" "i" "h"
# [20] "g" "f" "e" "d" "c" "b" "a"

# 按拼音排序
str_sort(c('你','好','粉','丝','日','志'),locale = "zh")  
# [1] "粉" "好" "你" "日" "丝" "志"

# More examples
str_order(letters)
str_sort(letters)
str_order(letters, locale = "haw")
str_sort(letters, locale = "haw")

x <- c("100a10", "100a5", "2b", "2a")
str_sort(x)
str_sort(x, numeric = TRUE)

对NA值的排序处理

#把NA放最后面
str_sort(c(NA,'1',NA),na_last=TRUE) 
# [1] "1" NA  NA
 
#把NA放最前面
str_sort(c(NA,'1',NA),na_last=FALSE) 
# [1] NA  NA  "1"

#去掉NA值 
str_sort(c(NA,'1',NA),na_last=NA)    
# [1] "1"

3.3 字符串匹配函数

  • str_split: 字符串分割
  • str_split_fixed: 字符串分割,同str_split
  • str_subset: 返回匹配的字符串
  • word: 从文本中提取单词
  • str_detect: 检查匹配字符串的字符
  • str_match: 从字符串中提取匹配组。
  • str_match_all: 从字符串中提取匹配组,同str_match
  • str_replace: 字符串替换
  • str_replace_all: 字符串替换,同str_replace
  • str_replace_na:把NA替换为NA字符串
  • str_locate: 找到匹配的字符串的位置。
  • str_locate_all: 找到匹配的字符串的位置,同str_locate
  • str_extract: 从字符串中提取匹配字符
  • str_extract_all: 从字符串中提取匹配字符,同str_extract

3.3.1 str_split,字符串分割,同str_split_fixed

函数定义-Description & Usage

str_split(string, pattern, n = Inf) str_split_fixed(string, pattern, n)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配的字符。
  • n: 分割个数

对字符串进行分割。Split up a string into pieces.

val <- "abc,123,234,iuuu"

# 以,进行分割
s1<-str_split(val, ",");s1
# [[1]]
# [1] "abc"  "123"  "234"  "iuuu"

# 以,进行分割,保留2块
s2<-str_split(val, ",",2);s2
# [[1]]
# [1] "abc"          "123,234,iuuu"

# 查看str_split()函数操作的结果类型list
class(s1)
# [1] "list"

# 用str_split_fixed()函数分割,结果类型是matrix
s3<-str_split_fixed(val, ",",2);s3
     # [,1]  [,2]          
# [1,] "abc" "123,234,iuuu"

class(s3)
# [1] "matrix"

# more examples
fruits <- c(
  "apples and oranges and pears and bananas",
  "pineapples and mangos and guavas"
)

str_split(fruits, " and ")
# [[1]]
# [1] "apples"  "oranges" "pears"   "bananas"
# 
# [[2]]
# [1] "pineapples" "mangos"     "guavas"  

str_split(fruits, " and ", simplify = TRUE)
#      [,1]         [,2]      [,3]     [,4]     
# [1,] "apples"     "oranges" "pears"  "bananas"
# [2,] "pineapples" "mangos"  "guavas" "" 

# Specify n to restrict the number of possible matches
str_split(fruits, " and ", n = 3)
# [[1]]
# [1] "apples"            "oranges"          
# [3] "pears and bananas"
# 
# [[2]]
# [1] "pineapples" "mangos"     "guavas" 

str_split(fruits, " and ", n = 2)
# [[1]]
# [1] "apples"                       
# [2] "oranges and pears and bananas"
# 
# [[2]]
# [1] "pineapples"        "mangos and guavas"

# If n greater than number of pieces, no padding occurs
str_split(fruits, " and ", n = 5)
# [[1]]
# [1] "apples"  "oranges" "pears"   "bananas"
# 
# [[2]]
# [1] "pineapples" "mangos"     "guavas"

# Use fixed to return a character matrix
str_split_fixed(fruits, " and ", 3)
#      [,1]         [,2]      [,3]               
# [1,] "apples"     "oranges" "pears and bananas"
# [2,] "pineapples" "mangos"  "guavas" 

str_split_fixed(fruits, " and ", 4)
#      [,1]         [,2]      [,3]     [,4]     
# [1,] "apples"     "oranges" "pears"  "bananas"
# [2,] "pineapples" "mangos"  "guavas" "" 

3.3.2 str_subset:返回的匹配字符串

函数定义-Description & Usage

str_subset(string, pattern)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配的字符。
val <- c("abc", 123, "cba")

# 全文匹配
str_subset(val, "a")
# [1] "abc" "cba"

# 开头匹配
str_subset(val, "^a")
# [1] "abc"

# 结尾匹配
str_subset(val, "a$")
# [1] "cba"

3.3.3 word, 从文本中提取单词

函数定义-Description & Usage

word(string, start = 1L, end = start, sep = fixed(" "))

参数列表-Arguments

  • string: 字符串,字符串向量。
  • start: 开始位置。
  • end: 结束位置。
  • sep: 匹配字符。

Extract words from a sentence.

val <- c("I am Conan.", "http://fens.me, ok")

# 默认以空格分割,取第一个位置的字符串
word(val, 1)
# [1] "I"               "http://fens.me,"
word(val, -1)
# [1] "Conan." "ok"    
word(val, 2, -1)
# [1] "am Conan." "ok"       

# 以,分割,取第一个位置的字符串 
val<-'111,222,333,444'
word(val, 1, sep = fixed(','))
# [1] "111"
word(val, 3, sep = fixed(','))
# [1] "333"

3.3.4 str_detect匹配字符串的字符

函数定义-Description & Usage

str_detect(string, pattern) 参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配字符。

Detect the presence or absence of a pattern in a string.

val <- c("abca4", 123, "cba2")

# 检查字符串向量,是否包括a
str_detect(val, "a")
# [1]  TRUE FALSE  TRUE

# 检查字符串向量,是否以a为开头
str_detect(val, "^a")
# [1]  TRUE FALSE FALSE

# 检查字符串向量,是否以a为结尾
str_detect(val, "a$")
# [1] FALSE FALSE FALSE

# More Examples
fruit <- c("apple", "banana", "pear", "pinapple");fruit
# [1] "apple"    "banana"   "pear"     "pinapple"
str_detect(fruit, "a")
# [1] TRUE TRUE TRUE TRUE
str_detect(fruit, "^a")
# [1]  TRUE FALSE FALSE FALSE
str_detect(fruit, "a$")
# [1] FALSE  TRUE FALSE FALSE
str_detect(fruit, "b")
# [1] FALSE  TRUE FALSE FALSE
str_detect(fruit, "[aeiou]")
# Also vectorised over pattern
str_detect("aecfg", letters)
#  [1]  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE
#  [9] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [25] FALSE FALSE

3.3.6 str_match,从字符串中提取匹配组

函数定义-Description & Usage

str_match(string, pattern) str_match_all(string, pattern)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配字符。

从字符串中提取匹配组Extract matched groups from a string.

val <- c("abc", 123, "cba")

# 匹配字符a,并返回对应的字符
str_match(val, "a")
#      [,1]
# [1,] "a" 
# [2,] NA  
# [3,] "a" 

# 匹配字符0-9,限1个,并返回对应的字符
str_match(val, "[0-9]")
#      [,1]
# [1,] NA  
# [2,] "1" 
# [3,] NA  

# 匹配字符0-9,不限数量,并返回对应的字符
str_match(val, "[0-9]*")
#      [,1] 
# [1,] ""   
# [2,] "123"
# [3,] ""  

strings <- c(" 219 733 8965", "329-293-8753 ", "banana", "595 794 7569",
  "387 287 6718", "apple", "233.398.9187  ", "482 952 3315",
  "239 923 8115 and 842 566 4692", "Work: 579-499-7527", "$1000",
  "Home: 543.355.3679");strings
# [1] " 219 733 8965"                
#  [2] "329-293-8753 "                
#  [3] "banana"                       
#  [4] "595 794 7569"                 
#  [5] "387 287 6718"                 
#  [6] "apple"                        
#  [7] "233.398.9187  "               
#  [8] "482 952 3315"                 
#  [9] "239 923 8115 and 842 566 4692"
# [10] "Work: 579-499-7527"           
# [11] "$1000"                        
# [12] "Home: 543.355.3679" 
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})";phone

str_extract(strings, phone)
# [1] "219 733 8965" "329-293-8753" NA            
#  [4] "595 794 7569" "387 287 6718" NA            
#  [7] "233.398.9187" "482 952 3315" "239 923 8115"
# [10] "579-499-7527" NA             "543.355.3679"

str_match(strings, phone)
#     [,1]           [,2]  [,3]  [,4]  
#  [1,] "219 733 8965" "219" "733" "8965"
#  [2,] "329-293-8753" "329" "293" "8753"
#  [3,] NA             NA    NA    NA    
#  [4,] "595 794 7569" "595" "794" "7569"
#  [5,] "387 287 6718" "387" "287" "6718"
#  [6,] NA             NA    NA    NA    
#  [7,] "233.398.9187" "233" "398" "9187"
#  [8,] "482 952 3315" "482" "952" "3315"
#  [9,] "239 923 8115" "239" "923" "8115"
# [10,] "579-499-7527" "579" "499" "7527"
# [11,] NA             NA    NA    NA    
# [12,] "543.355.3679" "543" "355" "3679"

# Extract/match all
str_extract_all(strings, phone)
# [[1]]
# [1] "219 733 8965"
# 
# [[2]]
# [1] "329-293-8753"
# 
# [[3]]
# character(0)
# 
# [[4]]
# [1] "595 794 7569"
# 
# [[5]]
# [1] "387 287 6718"
# 
# [[6]]
# character(0)
# 
# [[7]]
# [1] "233.398.9187"
# 
# [[8]]
# [1] "482 952 3315"
# 
# [[9]]
# [1] "239 923 8115" "842 566 4692"
# 
# [[10]]
# [1] "579-499-7527"
# 
# [[11]]
# character(0)
# 
# [[12]]
# [1] "543.355.3679"

str_match_all(strings, phone)
# [[1]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "219 733 8965" "219" "733" "8965"
# 
# [[2]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "329-293-8753" "329" "293" "8753"
# 
# [[3]]
#      [,1] [,2] [,3] [,4]
# 
# [[4]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "595 794 7569" "595" "794" "7569"
# 
# [[5]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "387 287 6718" "387" "287" "6718"
# 
# [[6]]
#      [,1] [,2] [,3] [,4]
# 
# [[7]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "233.398.9187" "233" "398" "9187"
# 
# [[8]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "482 952 3315" "482" "952" "3315"
# 
# [[9]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "239 923 8115" "239" "923" "8115"
# [2,] "842 566 4692" "842" "566" "4692"
# 
# [[10]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "579-499-7527" "579" "499" "7527"
# 
# [[11]]
#      [,1] [,2] [,3] [,4]
# 
# [[12]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "543.355.3679" "543" "355" "3679"

从字符串中提取匹配组,以字符串matrix格式返回


str_match_all(val, "a")
# [[1]]
#      [,1]
# [1,] "a" 
# 
# [[2]]
#      [,1]
# 
# [[3]]
#      [,1]
# [1,] "a" 

str_match_all(val, "[0-9]")
# [[1]]
#      [,1]
# 
# [[2]]
#      [,1]
# [1,] "1" 
# [2,] "2" 
# [3,] "3" 
# 
# [[3]]
#      [,1]

x <- c("<a> <b>", "<a> <>", "<a>", "", NA);x
str_match(x, "<(.*?)> <(.*?)>")
#      [,1]      [,2] [,3]
# [1,] "<a> <b>" "a"  "b" 
# [2,] "<a> <>"  "a"  ""  
# [3,] NA        NA   NA  
# [4,] NA        NA   NA  
# [5,] NA        NA   NA  

str_match_all(x, "<(.*?)>")
# [[1]]
#      [,1]  [,2]
# [1,] "<a>" "a" 
# [2,] "<b>" "b" 
# 
# [[2]]
#      [,1]  [,2]
# [1,] "<a>" "a" 
# [2,] "<>"  ""  
# 
# [[3]]
#      [,1]  [,2]
# [1,] "<a>" "a" 
# 
# [[4]]
#      [,1] [,2]
# 
# [[5]]
#      [,1] [,2]
# [1,] NA   NA  

str_extract(x, "<.*?>")
# [1] "<a>" "<a>" "<a>" NA    NA   
str_extract_all(x, "<.*?>")
# [[1]]
# [1] "<a>" "<b>"
# 
# [[2]]
# [1] "<a>" "<>" 
# 
# [[3]]
# [1] "<a>"
# 
# [[4]]
# character(0)
# 
# [[5]]
# [1] NA

3.3.7 str_replace,字符串替换

函数定义-Description & Usage

str_replace(string, pattern, replacement)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配字符。
  • replacement: 用于替换的字符。

Replace matched patterns in a string.

fruits %>%
  str_c(collapse = "---") %>%
  str_replace_all(c("one" = "1", "two" = "2", "three" = "3"))
[1] "1 apple---2 pears---3 bananas"

3.3.8 str_replace_na把NA替换为NA字符串

函数定义-Description & Usage

str_replace_na(string, replacement = "NA")

参数列表-Arguments

  • string: 字符串,字符串向量。
  • replacement : 用于替换的字符。

把NA替换为字符串Turn NA into “NA”

str_replace_na(c(NA,'NA',"abc"),'x')
# [1] "x"   "NA"  "abc"

str_replace_na(c(NA, "abc", "def"))
# [1] "NA"  "abc" "def"

3.3.9 str_locate,找到的模式在字符串中的位置。

函数定义-Description & Usage

str_locate(string, pattern) str_locate_all(string, pattern)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配字符。

Locate the position of patterns in a string.

val <- c("abca", 123, "cba")

# 匹配a在字符串中的位置
str_locate(val, "a")
#      start end
# [1,]     1   1
# [2,]    NA  NA
# [3,]     3   3

# 用向量匹配
str_locate(val, c("a", 12, "b"))
#      start end
# [1,]     1   1
# [2,]     1   2
# [3,]     2   2

# 以字符串matrix格式返回
str_locate_all(val, "a")
# [[1]]
#      start end
# [1,]     1   1
# [2,]     4   4
# 
# [[2]]
#      start end
# 
# [[3]]
#      start end
# [1,]     3   3

# 匹配a或b字符,以字符串matrix格式返回
str_locate_all(val, "[ab]")
# [[1]]
#      start end
# [1,]     1   1
# [2,]     2   2
# [3,]     4   4
# 
# [[2]]
#      start end
# 
# [[3]]
#      start end
# [1,]     2   2
# [2,]     3   3

# More Examples
fruit <- c("apple", "banana", "pear", "pineapple");fruit
# [1] "apple"     "banana"    "pear"      "pineapple"
str_locate(fruit, "$")
#      start end
# [1,]     6   5
# [2,]     7   6
# [3,]     5   4
# [4,]    10   9
str_locate(fruit, "a")
#      start end
# [1,]     1   1
# [2,]     2   2
# [3,]     3   3
# [4,]     5   5
str_locate(fruit, "e")
str_locate(fruit, c("a", "b", "p", "p"))

str_locate_all(fruit, "a")
str_locate_all(fruit, "e")
str_locate_all(fruit, c("a", "b", "p", "p"))

# Find location of every character
str_locate_all(fruit, "")

3.3.10 str_extract从字符串中提取匹配模式

函数定义-Description & Usage

str_extract(string, pattern) str_extract_all(string, pattern, simplify = FALSE)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • pattern: 匹配字符。
  • simplify: 返回值,TRUE返回matrixFALSE返回字符串向量

Extract matching patterns from a string.

val <- c("abca4", 123, "cba2")

# 返回匹配的数字
str_extract(val, "\\d")
# [1] "4" "1" "2"

# 返回匹配的字符
str_extract(val, "[a-z]+")
# [1] "abca" NA     "cba" 


val <- c("abca4", 123, "cba2")
str_extract_all(val, "\\d")
# [[1]]
# [1] "4"
# 
# [[2]]
# [1] "1" "2" "3"
# 
# [[3]]
# [1] "2"

str_extract_all(val, "[a-z]+")
# [[1]]
# [1] "abca"
# 
# [[2]]
# character(0)
# 
# [[3]]
# [1] "cba"

# More Examples
shopping_list <- c("apples x4", "bag of flour", "bag of sugar", "milk x2")
# extract digits
str_extract(shopping_list, "\\d")
# [1] "4" NA  NA  "2"

# extract character
str_extract(shopping_list, "[a-z]+")
# [1] "apples" "bag"    "bag"    "milk" 

# extract 4 characters
str_extract(shopping_list, "[a-z]{1,4}")
# [1] "appl" "bag"  "bag"  "milk"

str_extract(shopping_list, "\\b[a-z]{1,4}\\b")
# [1] NA     "bag"  "bag"  "milk"

# Extract all matches
str_extract_all(shopping_list, "[a-z]+")
str_extract_all(shopping_list, "\\b[a-z]+\\b")
str_extract_all(shopping_list, "\\d")

# Simplify results into character matrix
str_extract_all(shopping_list, "\\b[a-z]+\\b", simplify = TRUE)
str_extract_all(shopping_list, "\\d", simplify = TRUE)

# Extract all words
str_extract_all("This is, suprisingly, a sentence.", boundary("word"))

3.4 字符串变换函数

  • str_conv: 字符编码转换
  • str_to_upper: 字符串转成大写
  • str_to_lower: 字符串转成小写,规则同str_to_upper
  • str_to_title: 字符串转成首字母大写,规则同str_to_upper

3.4.1 str_conv:字符编码转换

函数定义-Description & Usage

str_conv(string, encoding)

参数列表-Arguments

  • string: 字符串,字符串向量。
  • encoding: 编码名。

对中文进行转码处理。Specify the encoding of a string.

# Example from encoding?stringi::stringi
x <- rawToChar(as.raw(177));x
# [1] "?
str_conv(x, "ISO-8859-2") # Polish "a with ogonek"
# [1] "ą"
str_conv(x, "ISO-8859-1") # Plus-minus
# [1] "±"

# 把中文字符字节化
x <- charToRaw('你好');x
# [1] c4 e3 ba c3

# 默认RStudio系统字符集为UTF-8
str_conv(x, "UTF-8")
# [1] "你好"

# 默认win系统字符集为GBK,GB2312为GBK字集,转码NO正常
str_conv(x, "GBK")
# [1] "浣犲ソ"
str_conv(x, "GB2312")
# input data \xffffffa0 in current source encoding could not be converted to Unicodeinput data \xffffffbd in current source encoding could not be converted to Unicode[1] "浣\032濂\032"

# 转UTF-8失败
str_conv(x, "UTF-8")
# [1] "���"
# Warning messages:
# 1: In stri_conv(string, encoding, "UTF-8") :
#   input data \xffffffc4 in current source encoding could not be converted to Unicode
# 2: In stri_conv(string, encoding, "UTF-8") :
#   input data \xffffffe3\xffffffba in current source encoding could not be converted to Unicode
# 3: In stri_conv(string, encoding, "UTF-8") :
#   input data \xffffffc3 in current source encoding could not be converted to Unicode

把unicode转UTF-8

x1 <- "\u5317\u4eac"

str_conv(x1, "UTF-8")
# [1] "北京"

3.4.2 str_to_upper,字符串大写转换。

函数定义-Description & Usage

str_to_upper(string, locale = "") str_to_lower(string, locale = "") str_to_title(string, locale = "")

参数列表-Arguments

  • string: 字符串。
  • locale:按哪种语言习惯排序

字符串大写转换Convert case of a string.:

val <- "I am conan. Welcome to my blog! http://fens.me"

# 全大写
str_to_upper(val)
# [1] "I AM CONAN. WELCOME TO MY BLOG! HTTP://FENS.ME"

# 全小写
str_to_lower(val)
# [1] "i am conan. welcome to my blog! http://fens.me"

# 首字母大写
str_to_title(val)
# [1] "I Am Conan. Welcome To My Blog! Http://Fens.Me"

# More Examples
dog <- "The quick brown dog";dog
str_to_upper(dog)
# [1] "THE QUICK BROWN DOG"
str_to_lower(dog)
# [1] "the quick brown dog"
str_to_title(dog)
# [1] "The Quick Brown Dog"

# Locale matters! 
str_to_upper("i") # English
str_to_upper("i", "tr") # Turkish

4. 参数控制函数(仅用于构造功能的参数,不能独立使用)。

  • boundary: 定义使用边界
  • coll: 定义字符串标准排序规则。
  • fixed: 定义用于匹配的字符,包括正则表达式中的转义符
  • regex: 定义正则表达式

5. 总结

字符串在平常的数据处理中经常用过,需要对字符串进行分割、连接、转换等操作,本篇中通过介绍stringr,灵活的字符串处理库,可以有效地提高代码的编写效率。有了好的工具,在用R语言处理字符串就顺手了。

转载请注明出处:http://blog.fens.me/r-stringr/

6. 其他代码

#生成数据  
set.seed(123);  
n = 5000000;  
p = 5;  
system.time(x <- matrix(rnorm(n * p), n, p));  
x = cbind(1, x);  
bet = c(2, rep(1, p));  
y = c(x %*% bet) + rnorm(n);  
# Garbage Collection
gc();  
dat = as.data.frame(x);  
rm(x);  
gc();  
dat$y = y;  
rm(y);  
gc();  
colnames(dat) = c(paste("x", 0:p, sep = ""), "y");  
gc();  
?gc()
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBzdHJpbmdyIHBhY2thZ2UiDQphdXRob3I6ICJTdG9uZV9Ib3UiDQpkYXRlOiAiMjAxN+W5tDfmnIg55pelIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQotLS0NCg0KIyBbU3RyaW5ncl0oaHR0cDovL2Jsb2cuZmVucy5tZS9yLXN0cmluZ3IvKSBJbnRyb2R1Y3Rpb24gZnJvbSBbaHR0cDovL2Jsb2cuZmVucy5tZS9yLXN0cmluZ3IvXShodHRwOi8vYmxvZy5mZW5zLm1lL3Itc3RyaW5nci8pLg0KDQo+IFJlZmVyZW5jZTogW2h0dHA6Ly9ibG9nLmZlbnMubWUvci1zdHJpbmdyL10oaHR0cDovL2Jsb2cuZmVucy5tZS9yLXN0cmluZ3IvKQ0KDQojIyDliY3oqIANCg0K55SoUuivreiogOWkhOeQhuWtl+espuS4su+8jOaAu+inieW+l+W+iOm6u+eDpu+8jOWNs+S4jeiDveeUqOWQkemHj+eahOaWueazlei/m+ihjOWIhuWJsu+8jOS5n+S4jeiDveeUqOW+queOr+mBjeWOhue0ouW8leOAgmdyZXAoKeWutuaXj+WHveaVsOW4uOW4uOiusOS4jeS9j++8jHBhc3RlKCnlh73mlbDpu5jorqTku6XnqbrmoLzliIblibLvvIzlkITnp43kuI3pobrmiYvllYrvvIHpmo/nnYDkvb/nlKhS6K+t6KiA55qE5Zy65pmv6LaK5p2l6LaK5aSa77yM5a2X56ym5Liy5aSE55CG5piv5b+F5LiN5Y+v5bCR55qE44CC57uZ5aSn5a625o6o6I2Q5LiA5Liq55SxIEhhZGxleSBXaWNraGFtIOW8gOWPkeeahOS4gOS4queBtea0u+eahOWtl+espuS4suWkhOeQhuWMhXN0cmluZ3LjgIINCg0KIyMg55uu5b2VDQoNCnN0cmluZ3Lku4vnu40NCg0Kc3RyaW5ncuWuieijhQ0KDQpzdHJpbmdy55qEQVBJ5LuL57uNDQoNCiMjIDEuIHN0cmluZ3Lku4vnu40NCg0Kc3RyaW5ncuWMheiiq+WumuS5ieS4uuS4gOiHtOeahOOAgeeugOWNleaYk+eUqOeahOWtl+espuS4suW3peWFt+mbhuOAguaJgOacieeahOWHveaVsOWSjOWPguaVsOWumuS5iemDveWFt+acieS4gOiHtOaAp++8jOavlOWmgu+8jOeUqOebuOWQjOeahOaWueazlei/m+ihjE5B5aSE55CG5ZKMMOmVv+W6pueahOWQkemHj+WkhOeQhuOAgg0KDQrlrZfnrKbkuLLlpITnkIbomb3nhLbkuI3mmK9S6K+t6KiA5Lit5pyA5Li76KaB55qE5Yqf6IO977yM5Y205Lmf5piv5b+F5LiN5Y+v5bCR55qE77yM5pWw5o2u5riF5rSX44CB5Y+v6KeG5YyW562J55qE5pON5L2c6YO95Lya55So5Yiw44CC5a+55LqOUuivreiogOacrOi6q+eahGJhc2XljIXmj5DkvpvnmoTlrZfnrKbkuLLln7rnoYDlh73mlbDvvIzpmo/nnYDml7bpl7TnmoTnp6/ntK/vvIzlt7Lnu4/lj5jlvpflvojlpJrlnLDmlrnkuI3kuIDoh7TvvIzkuI3op4TojIPnmoTlkb3lkI3vvIzkuI3moIflh4bnmoTlj4LmlbDlrprkuYnvvIzlvojpmr7nnIvkuIDnnLzlsLHkuIrmiYvkvb/nlKjjgILlrZfnrKbkuLLlpITnkIblnKjlhbbku5bor63oqIDkuK3pg73mmK/pnZ7luLjmlrnkvr/nmoTkuovmg4XvvIxS6K+t6KiA5Zyo6L+Z5pa56Z2i56Gu5a6e6JC95ZCO5LqG44CCc3RyaW5ncuWMheWwseaYr+S4uuS6huino+WGs+i/meS4qumXrumimO+8jOiuqeWtl+espuS4suWkhOeQhuWPmOW+l+eugOWNleaYk+eUqO+8jOaPkOS+m+WPi+WlveeahOWtl+espuS4suaTjeS9nOaOpeWPo+OAgg0KDQpzdHJpbmdy55qE6aG555uu5Li76aG177yaW2h0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zdHJpbmdyL2luZGV4Lmh0bWxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zdHJpbmdyL2luZGV4Lmh0bWwpDQoNCiMjIDIuIHN0cmluZ3Llronoo4UNCg0K5pys5paH5omA5L2/55So55qE57O757uf546v5aKDDQoNCldpbjEwIDY0Yml0DQpSOiAzLjIuMyB4ODZfNjQtdzY0LW1pbmd3MzIveDY0IGI0Yml0DQoNCnN0cmluZ3LmmK/lnKhDUkFO5Y+R5biD55qE5qCH5YeG5bqT77yM5a6J6KOF6LW35p2l6Z2e5bi4566A5Y2V77yMMuadoeWRveS7pOWwseWPr+S7peS6huOAgg0KDQpgYGB7ciBpbnN0YWxsIHN0cmluZ3IgcGFja2FnZX0NCiMgfiBSDQojIExvYWQgc3RyaW5nciBwYWNrYWdlDQppZighc3VwcHJlc3NXYXJuaW5ncyhyZXF1aXJlKCJzdHJpbmdyIikpKQ0Kew0KICAgIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQ0KICAgIHJlcXVpcmUoInN0cmluZ3IiKQ0KfQ0KDQojIH4gUg0KIyBpbnN0YWxsLnBhY2thZ2VzKCdzdHJpbmdyJykNCiMgbGlicmFyeShzdHJpbmdyKQ0KYGBgDQoNCiMjIDMuIHN0cmluZ3LnmoRBUEnku4vnu40NCg0Kc3RyaW5ncuWMhTEuMC4w54mI5pys77yM5LiA5YWx5o+Q5L6b5LqGMzDkuKrlh73mlbDvvIzmlrnkvr/miJHku6zlr7nlrZfnrKbkuLLlpITnkIbjgILluLjnlKjnmoTlrZfnrKbkuLLnmoTlpITnkIbku6VzdHJf5byA5aS05p2l5ZG95ZCN77yM5pa55L6/5pu055u06KeC55CG6Kej5Ye95pWw55qE5a6a5LmJ44CC5oiR5Lus5Y+v5Lul5qC55o2u5L2/55So5Lmg5oOv5a+55Ye95pWw6L+b6KGM5YiG57G777yaDQoNCiMjIyAzLjEg5a2X56ym5Liy5ou85o6l5Ye95pWwDQoNCiogYHN0cl9jYDoJ5a2X56ym5Liy5ou85o6l44CCDQoqIGBzdHJfam9pbmA6CeWtl+espuS4suaLvOaOpe+8jOWQjGBzdHJfY2DjgIINCiogYHN0cl90cmltYDoJ5Y675o6J5a2X56ym5Liy55qE56m65qC85ZKMVEFCKFx0KQ0KKiBgc3RyX3BhZGA6CeihpeWFheWtl+espuS4sueahOmVv+W6pg0KKiBgc3RyX2R1cGA6CeWkjeWItuWtl+espuS4sg0KKiBgc3RyX3dyYXBgOgnmjqfliLblrZfnrKbkuLLovpPlh7rmoLzlvI8NCiogYHN0cl9zdWJgOgnmiKrlj5blrZfnrKbkuLINCiogYHN0cl9zdWJgOgnmiKrlj5blrZfnrKbkuLLvvIzlubbotYvlgLzvvIzlkIxgc3RyX3N1YmANCg0KDQojIyMjIDMuMS4xIHN0cl9j77yM5a2X56ym5Liy5ou85o6l5pON5L2c77yM5LiOc3RyX2pvaW7lrozlhajnm7jlkIzvvIzkuI5wYXN0ZSgp6KGM5Li65LiN5a6M5YWo5LiA6Ie044CCDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX2MoLi4uLCBzZXAgPSAiIiwgY29sbGFwc2UgPSBOVUxMKWANCmBzdHJfam9pbiguLi4sIHNlcCA9ICIiLCBjb2xsYXBzZSA9IE5VTEwpYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGDigKZgOiDlpJrlj4LmlbDnmoTovpPlhaUNCiogYHNlcGA6IOaKiuWkmuS4quWtl+espuS4suaLvOaOpeS4uuS4gOS4quWkp+eahOWtl+espuS4su+8jOeUqOS6juWtl+espuS4sueahOWIhuWJsuespuOAgg0KKiBgY29sbGFwc2VgOiDmiorlpJrkuKrlkJHph4/lj4LmlbDmi7zmjqXkuLrkuIDkuKrlpKfnmoTlrZfnrKbkuLLvvIznlKjkuo7lrZfnrKbkuLLnmoTliIblibLnrKbjgIINCg0K5oqK5aSa5Liq5a2X56ym5Liy5ou85o6l5Li65LiA5Liq5aSn55qE5a2X56ym5Liy44CCSm9pbiBtdWx0aXBsZSBzdHJpbmdzIGludG8gYSBzaW5nbGUgc3RyaW5nLg0KDQpgYGB7ciBzdHJfYyBleDEgc2VwIHBhcmFtZXRlcn0NCg0Kc3RyX2MoJ2EnLCdiJykNCiMgWzFdICJhYiINCnN0cl9jKCdhJywnYicsc2VwPSctJykNCiMgWzFdICJhLWIiDQpzdHJfYyhjKCdhJywnYTEnKSxjKCdiJywnYjEnKSxzZXA9Jy0nKQ0KIyBbMV0gImEtYiIgICAiYTEtYjEiDQoNCiMgTW9yZSBFeGFtcGxlcw0Kc3RyX2MoIkxldHRlcjogIiwgbGV0dGVycykNCiMgWzFdICJMZXR0ZXI6IGEiICJMZXR0ZXI6IGIiICJMZXR0ZXI6IGMiICJMZXR0ZXI6IGQiDQojICBbNV0gIkxldHRlcjogZSIgIkxldHRlcjogZiIgIkxldHRlcjogZyIgIkxldHRlcjogaCINCiMgIFs5XSAiTGV0dGVyOiBpIiAiTGV0dGVyOiBqIiAiTGV0dGVyOiBrIiAiTGV0dGVyOiBsIg0KIyBbMTNdICJMZXR0ZXI6IG0iICJMZXR0ZXI6IG4iICJMZXR0ZXI6IG8iICJMZXR0ZXI6IHAiDQojIFsxN10gIkxldHRlcjogcSIgIkxldHRlcjogciIgIkxldHRlcjogcyIgIkxldHRlcjogdCINCiMgWzIxXSAiTGV0dGVyOiB1IiAiTGV0dGVyOiB2IiAiTGV0dGVyOiB3IiAiTGV0dGVyOiB4Ig0KIyBbMjVdICJMZXR0ZXI6IHkiICJMZXR0ZXI6IHoiDQoNCnN0cl9jKCJMZXR0ZXIiLCBsZXR0ZXJzLCBzZXAgPSAiOiAiKQ0KIyBbMV0gIkxldHRlcjogYSIgIkxldHRlcjogYiIgIkxldHRlcjogYyIgIkxldHRlcjogZCINCiMgIFs1XSAiTGV0dGVyOiBlIiAiTGV0dGVyOiBmIiAiTGV0dGVyOiBnIiAiTGV0dGVyOiBoIg0KIyAgWzldICJMZXR0ZXI6IGkiICJMZXR0ZXI6IGoiICJMZXR0ZXI6IGsiICJMZXR0ZXI6IGwiDQojIFsxM10gIkxldHRlcjogbSIgIkxldHRlcjogbiIgIkxldHRlcjogbyIgIkxldHRlcjogcCINCiMgWzE3XSAiTGV0dGVyOiBxIiAiTGV0dGVyOiByIiAiTGV0dGVyOiBzIiAiTGV0dGVyOiB0Ig0KIyBbMjFdICJMZXR0ZXI6IHUiICJMZXR0ZXI6IHYiICJMZXR0ZXI6IHciICJMZXR0ZXI6IHgiDQojIFsyNV0gIkxldHRlcjogeSIgIkxldHRlcjogeiINCg0Kc3RyX2MobGV0dGVycywgIiBpcyBmb3IiLCAiLi4uIikNCiMgWzFdICJhIGlzIGZvci4uLiIgImIgaXMgZm9yLi4uIiAiYyBpcyBmb3IuLi4iDQojICBbNF0gImQgaXMgZm9yLi4uIiAiZSBpcyBmb3IuLi4iICJmIGlzIGZvci4uLiINCiMgIFs3XSAiZyBpcyBmb3IuLi4iICJoIGlzIGZvci4uLiIgImkgaXMgZm9yLi4uIg0KIyBbMTBdICJqIGlzIGZvci4uLiIgImsgaXMgZm9yLi4uIiAibCBpcyBmb3IuLi4iDQojIFsxM10gIm0gaXMgZm9yLi4uIiAibiBpcyBmb3IuLi4iICJvIGlzIGZvci4uLiINCiMgWzE2XSAicCBpcyBmb3IuLi4iICJxIGlzIGZvci4uLiIgInIgaXMgZm9yLi4uIg0KIyBbMTldICJzIGlzIGZvci4uLiIgInQgaXMgZm9yLi4uIiAidSBpcyBmb3IuLi4iDQojIFsyMl0gInYgaXMgZm9yLi4uIiAidyBpcyBmb3IuLi4iICJ4IGlzIGZvci4uLiINCiMgWzI1XSAieSBpcyBmb3IuLi4iICJ6IGlzIGZvci4uLiINCg0Kc3RyX2MobGV0dGVyc1stMjZdLCAiIGNvbWVzIGJlZm9yZSAiLCBsZXR0ZXJzWy0xXSkNCiMgIFsxXSAiYSBjb21lcyBiZWZvcmUgYiIgImIgY29tZXMgYmVmb3JlIGMiDQojICBbM10gImMgY29tZXMgYmVmb3JlIGQiICJkIGNvbWVzIGJlZm9yZSBlIg0KIyAgWzVdICJlIGNvbWVzIGJlZm9yZSBmIiAiZiBjb21lcyBiZWZvcmUgZyINCiMgIFs3XSAiZyBjb21lcyBiZWZvcmUgaCIgImggY29tZXMgYmVmb3JlIGkiDQojICBbOV0gImkgY29tZXMgYmVmb3JlIGoiICJqIGNvbWVzIGJlZm9yZSBrIg0KIyBbMTFdICJrIGNvbWVzIGJlZm9yZSBsIiAibCBjb21lcyBiZWZvcmUgbSINCiMgWzEzXSAibSBjb21lcyBiZWZvcmUgbiIgIm4gY29tZXMgYmVmb3JlIG8iDQojIFsxNV0gIm8gY29tZXMgYmVmb3JlIHAiICJwIGNvbWVzIGJlZm9yZSBxIg0KIyBbMTddICJxIGNvbWVzIGJlZm9yZSByIiAiciBjb21lcyBiZWZvcmUgcyINCiMgWzE5XSAicyBjb21lcyBiZWZvcmUgdCIgInQgY29tZXMgYmVmb3JlIHUiDQojIFsyMV0gInUgY29tZXMgYmVmb3JlIHYiICJ2IGNvbWVzIGJlZm9yZSB3Ig0KIyBbMjNdICJ3IGNvbWVzIGJlZm9yZSB4IiAieCBjb21lcyBiZWZvcmUgeSINCiMgWzI1XSAieSBjb21lcyBiZWZvcmUgeiINCg0KYGBgDQoNCuaKiuWkmuS4quWQkemHj+WPguaVsOaLvOaOpeS4uuS4gOS4quWkp+eahOWtl+espuS4suOAgg0KDQpgYGB7ciBzdHJfYyBleDIgY29sbGFwc2UgcGFyYW1ldGVyfQ0KDQpzdHJfYyhoZWFkKGxldHRlcnMpLCBjb2xsYXBzZSA9ICIiKQ0KIyBbMV0gImFiY2RlZiINCnN0cl9jKGhlYWQobGV0dGVycyksIGNvbGxhcHNlID0gIiwgIikNCiMgWzFdICJhLCBiLCBjLCBkLCBlLCBmIg0KDQojIGNvbGxhcHNl5Y+C5pWw77yM5a+55aSa5Liq5a2X56ym5Liy5peg5pWIDQpzdHJfYygnYScsJ2InLGNvbGxhcHNlID0gIi0iKSAgIA0KIyBbMV0gImFiIg0Kc3RyX2MoYygnYScsJ2ExJyksYygnYicsJ2IxJyksY29sbGFwc2U9Jy0nKQ0KIyBbMV0gImFiLWExYjEiDQpgYGANCg0K5ou85o6l5pyJTkHlgLznmoTlrZfnrKbkuLLlkJHph4/ml7bvvIxOQei/mOaYr05BDQoNCmBgYHtyIHN0cl9jIGV4MyBOQX0NCiMgTWlzc2luZyBpbnB1dHMgZ2l2ZSBtaXNzaW5nIG91dHB1dHMNCnN0cl9jKGMoImEiLCBOQSwgImIiKSwgIi1kIikNCiMgWzFdICJhLWQiIE5BICAgICJiLWQiDQoNCiMgVXNlIHN0cl9yZXBsYWNlX05BIHRvIGRpc3BsYXkgbGl0ZXJhbCBOQXM6DQpzdHJfYyhzdHJfcmVwbGFjZV9uYShjKCJhIiwgTkEsICJiIikpLCAiLWQiKQ0KIyBbMV0gImEtZCIgICJOQS1kIiAiYi1kIiANCmBgYA0KDQrlr7nmr5Rgc3RyX2MoKWDlh73mlbDlkoxgcGFzdGUoKWDlh73mlbDkuYvpl7TnmoTkuI3lkIzngrnjgIINCg0KYGBge3Igc3RyX2MgZXg0IHZzIHBhc3RlfQ0KIyDlpJrlrZfnrKbkuLLmi7zmjqXvvIzpu5jorqTnmoRzZXDlj4LmlbDooYzkuLrkuI3kuIDoh7QNCnN0cl9jKCdhJywnYicpDQojIFsxXSAiYWIiDQpwYXN0ZSgnYScsJ2InKQ0KIyBbMV0gImEgYiINCg0KIyDlkJHph4/mi7zmjqXlrZfnrKbkuLLvvIxjb2xsYXBzZeWPguaVsOeahOihjOS4uuS4gOiHtA0Kc3RyX2MoaGVhZChsZXR0ZXJzKSwgY29sbGFwc2UgPSAiIikNCiMgWzFdICJhYmNkZWYiDQpwYXN0ZShoZWFkKGxldHRlcnMpLCBjb2xsYXBzZSA9ICIiKQ0KIyBbMV0gImFiY2RlZiINCiANCiPmi7zmjqXmnIlOQeWAvOeahOWtl+espuS4suWQkemHj++8jOWvuU5B55qE5aSE55CG6KGM5Li65LiN5LiA6Ie0DQpzdHJfYyhjKCJhIiwgTkEsICJiIiksICItZCIpDQojIFsxXSAiYS1kIiBOQSAgICAiYi1kIg0KcGFzdGUoYygiYSIsIE5BLCAiYiIpLCAiLWQiKQ0KIyBbMV0gImEgLWQiICAiTkEgLWQiICJiIC1kIiANCmBgYA0KDQojIyMjIDMuMS4yIHN0cl90cmltOuWOu+aOieWtl+espuS4sueahOepuuagvOWSjFRBQihcdCkNCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfdHJpbShzdHJpbmcsIHNpZGUgPSBjKCJib3RoIiwgImxlZnQiLCAicmlnaHQiKSlgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgc2lkZWA6IOi/h+a7pOaWueW8j++8jGBib3RoYOS4pOi+uemDvei/h+a7pO+8jGBsZWZ0YOW3pui+uei/h+a7pO+8jGByaWdodGDlj7Povrnov4fmu6QNCg0K5Y675o6J5a2X56ym5Liy55qE56m65qC85ZKMVEFCKFx0KSxUcmltIHdoaXRlc3BhY2UgZnJvbSBzdGFydCBhbmQgZW5kIG9mIHN0cmluZy4NCg0KYGBge3Igc3RyX3RyaW0gZXgxfQ0KI+WPqui/h+a7pOW3pui+ueeahOepuuagvA0Kc3RyX3RyaW0oIiAgbGVmdCBzcGFjZVx0XG4iLHNpZGU9J2xlZnQnKSANCiMgWzFdICJsZWZ0IHNwYWNlXHRcbiINCg0KI+WPqui/h+a7pOWPs+i+ueeahOepuuagvA0Kc3RyX3RyaW0oIiAgbGVmdCBzcGFjZVx0XG4iLHNpZGU9J3JpZ2h0JykNCiMgWzFdICIgIGxlZnQgc3BhY2UiDQoNCiPov4fmu6TkuKTovrnnmoTnqbrmoLwNCnN0cl90cmltKCIgIGxlZnQgc3BhY2VcdFxuIixzaWRlPSdib3RoJykNCiMgWzFdICJsZWZ0IHNwYWNlIg0KDQoj6L+H5ruk5Lik6L6555qE56m65qC8LGRlZmF1bHQNCnN0cl90cmltKCJcbm5vIHNwYWNlXG5cdCIpDQojIFsxXSAibm8gc3BhY2UiDQoNCiMgTW9yZSBFeGFtcGxlcw0KY2F0KCIgIFN0cmluZyB3aXRoIHRyYWlsaW5nIGFuZCBsZWFkaW5nIHdoaXRlIHNwYWNlXHQiKQ0KIyAgU3RyaW5nIHdpdGggdHJhaWxpbmcgYW5kIGxlYWRpbmcgd2hpdGUgc3BhY2UJDQpzdHJfdHJpbSgiICBTdHJpbmcgd2l0aCB0cmFpbGluZyBhbmQgbGVhZGluZyB3aGl0ZSBzcGFjZVx0IikNCiMgWzFdICJTdHJpbmcgd2l0aCB0cmFpbGluZyBhbmQgbGVhZGluZyB3aGl0ZSBzcGFjZSINCg0KY2F0KCJcblxuU3RyaW5nIHdpdGggdHJhaWxpbmcgYW5kIGxlYWRpbmcgd2hpdGUgc3BhY2UgICAgXG5cbiIpDQojIA0KIyANCiMgU3RyaW5nIHdpdGggdHJhaWxpbmcgYW5kIGxlYWRpbmcgd2hpdGUgc3BhY2UgICAgDQoNCnN0cl90cmltKCJcblxuU3RyaW5nIHdpdGggdHJhaWxpbmcgYW5kIGxlYWRpbmcgd2hpdGUgc3BhY2UgICAgXG5cbiIpDQojIFsxXSAiU3RyaW5nIHdpdGggdHJhaWxpbmcgYW5kIGxlYWRpbmcgd2hpdGUgc3BhY2UiDQpgYGANCg0KDQojIyMjIDMuMS4zIHN0cl9wYWQ66KGl5YWF5a2X56ym5Liy55qE6ZW/5bqmDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX3BhZChzdHJpbmcsIHdpZHRoLCBzaWRlID0gYygibGVmdCIsICJyaWdodCIsICJib3RoIiksIHBhZCA9ICIgIilgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgd2lkdGhgOiDlrZfnrKbkuLLloavlhYXlkI7nmoTplb/luqYNCiogYHNpZGVgOiDloavlhYXmlrnlkJHvvIxgYm90aGDkuKTovrnpg73loavlhYXvvIxgbGVmdGDlt6bovrnloavlhYXvvIxgcmlnaHRg5Y+z6L655aGr5YWFDQoqIGBwYWRgOiDnlKjkuo7loavlhYXnmoTlrZfnrKYNCg0K6KGl5YWF5a2X56ym5Liy55qE6ZW/5bqm44CCUGFkIGEgc3RyaW5nDQoNCmBgYHtyIHN0cl9wYWQgZXgxfQ0KIyDku47lt6bovrnooaXlhYXnqbrmoLzvvIznm7TliLDlrZfnrKbkuLLplb/luqbkuLoyMA0Kc3RyX3BhZCgiU3RvbmVfSG91IiwgMjAsICJsZWZ0IikNCiMgWzFdICIgICAgICAgICAgIFN0b25lX0hvdSINCg0KIyDku47lj7PovrnooaXlhYXnqbrmoLzvvIznm7TliLDlrZfnrKbkuLLplb/luqbkuLoyMA0Kc3RyX3BhZCgiU3RvbmVfSG91IiwgMjAsICJyaWdodCIpDQojIFsxXSAiU3RvbmVfSG91ICAgICAgICAgICAiDQoNCiMg5LuO5bem5Y+z5Lik6L655ZCE6KGl5YWF56m65qC877yM55u05Yiw5a2X56ym5Liy6ZW/5bqm5Li6MjANCnN0cl9wYWQoIlN0b25lX0hvdSIsIDIwLCAiYm90aCIpDQojIFsxXSAiICAgICBTdG9uZV9Ib3UgICAgICAiDQoNCiMg5LuO5bem5Y+z5Lik6L655ZCE6KGl5YWFeOWtl+espu+8jOebtOWIsOWtl+espuS4sumVv+W6puS4ujIwDQpzdHJfcGFkKCJTdG9uZV9Ib3UiLCAyMCwgImJvdGgiLCd6JykNCiMgWzFdICJ6enp6elN0b25lX0hvdXp6enp6eiINCg0KIyBNb3JlIEV4YW1wbGVzDQpyYmluZCgNCiAgc3RyX3BhZCgiaGFkbGV5IiwgMzAsICJsZWZ0IiksDQogIHN0cl9wYWQoImhhZGxleSIsIDMwLCAicmlnaHQiKSwNCiAgc3RyX3BhZCgiaGFkbGV5IiwgMzAsICJib3RoIikNCikNCiMgICAgICBbLDFdICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KIyBbMSxdICIgICAgICAgICAgICAgICAgICAgICAgICBoYWRsZXkiDQojIFsyLF0gImhhZGxleSAgICAgICAgICAgICAgICAgICAgICAgICINCiMgWzMsXSAiICAgICAgICAgICAgaGFkbGV5ICAgICAgICAgICAgIg0KDQojIEFsbCBhcmd1bWVudHMgYXJlIHZlY3RvcmlzZWQgZXhjZXB0IHNpZGUNCnN0cl9wYWQoYygiYSIsICJhYmMiLCAiYWJjZGVmIiksIDEwKQ0KIyBbMV0gIiAgICAgICAgIGEiICIgICAgICAgYWJjIiAiICAgIGFiY2RlZiINCnN0cl9wYWQoImEiLCBjKDUsIDEwLCAyMCkpDQojIFsxXSAiICAgIGEiICAgICAgICAgICAgICAgICIgICAgICAgICBhIiAgICAgICAgICANCiMgWzNdICIgICAgICAgICAgICAgICAgICAgYSINCnN0cl9wYWQoImEiLCAxMCwgcGFkID0gYygiLSIsICJfIiwgIiAiKSkNCiMgWzFdICItLS0tLS0tLS1hIiAiX19fX19fX19fYSIgIiAgICAgICAgIGEiDQoNCiMgTG9uZ2VyIHN0cmluZ3MgdGhhbiB3aWR0aCBhcmUgcmV0dXJuZWQgdW5jaGFuZ2VkDQpzdHJfcGFkKCJoYWRsZXkiLCAzKQ0KIyBbMV0gImhhZGxleSINCmBgYA0KDQojIyMjIDMuMS40IHN0cl9kdXA6IOWkjeWItuWtl+espuS4sg0KDQoqKuWHveaVsOWumuS5iS1EZXNjcmlwdGlvbiAmIFVzYWdlKirvvJoNCg0KYHN0cl9kdXAoc3RyaW5nLCB0aW1lcylgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgdGltZXNgOiDlpI3liLbmlbDph48NCg0K5aSN5Yi25LiA5Liq5a2X56ym5Liy5ZCR6YeP44CCRHVwbGljYXRlIGFuZCBjb25jYXRlbmF0ZSBzdHJpbmdzIHdpdGhpbiBhIGNoYXJhY3RlciB2ZWN0b3INCg0KYGBge3Igc3RyX2R1cCBleDF9DQp2YWwgPC0gYygiYWJjYTQiLCAxMjMsICJjYmEyIikNCg0KIyDlpI3liLYy5qyhDQpzdHJfZHVwKHZhbCwgMikNCiMgWzFdICJhYmNhNGFiY2E0IiAiMTIzMTIzIiAgICAgImNiYTJjYmEyIiAgDQoNCiMg5oyJ5L2N572u5aSN5Yi2DQpzdHJfZHVwKHZhbCwgMTozKQ0KIyBbMV0gImFiY2E0IiAgICAgICAgIjEyMzEyMyIgICAgICAgImNiYTJjYmEyY2JhMiINCg0KIyBNb3JlIEV4YW1wbGVzDQpmcnVpdCA8LSBjKCJhcHBsZSIsICJwZWFyIiwgImJhbmFuYSIpO2ZydWl0DQojIFsxXSAiYXBwbGUiICAicGVhciIgICAiYmFuYW5hIg0Kc3RyX2R1cChmcnVpdCwgMikNCiMgWzFdICJhcHBsZWFwcGxlIiAgICJwZWFycGVhciIgICAgICJiYW5hbmFiYW5hbmEiDQpzdHJfZHVwKGZydWl0LCAxOjMpDQojIFsxXSAiYXBwbGUiICAgICAgICAgICAgICAicGVhcnBlYXIiICAgICAgICAgIA0KIyBbM10gImJhbmFuYWJhbmFuYWJhbmFuYSINCnN0cl9jKCJiYSIsIHN0cl9kdXAoIm5hIiwgMDo1KSkNCiMgWzFdICJiYSIgICAgICAgICAgICJiYW5hIiAgICAgICAgICJiYW5hbmEiICAgICAgDQojIFs0XSAiYmFuYW5hbmEiICAgICAiYmFuYW5hbmFuYSIgICAiYmFuYW5hbmFuYW5hIg0KYGBgDQoNCiMjIyMgMy4xLjUgc3RyX3dyYXDvvIzmjqfliLblrZfnrKbkuLLovpPlh7rmoLzlvI8NCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfd3JhcChzdHJpbmcsIHdpZHRoID0gODAsIGluZGVudCA9IDAsIGV4ZGVudCA9IDApYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHdpZHRoYDog6K6+572u5LiA6KGM5omA5Y2g55qE5a695bqm44CCDQoqIGBpbmRlbnRgOiDmrrXokL3pppbooYznmoTnvKnov5vlgLwNCiogYGV4ZGVudGA6IOauteiQvemdnummluihjOeahOe8qei/m+WAvA0KDQpgYGB7ciBzdHJfd3JhcCBleDF9DQp0eHQgPC0gJ1Lor63oqIDkvZzkuLrnu5/orqHlrabkuIDpl6jor63oqIDvvIzkuIDnm7TlnKjlsI/kvJfpoobln5/pl6rogIDnnYDlhYnoipLjgILnm7TliLDlpKfmlbDmja7nmoTniIblj5HvvIxS6K+t6KiA5Y+Y5oiQ5LqG5LiA6Zeo54KZ5omL5Y+v54Ot55qE5pWw5o2u5YiG5p6Q55qE5Yip5Zmo44CC6ZqP552A6LaK5p2l6LaK5aSa55qE5bel56iL6IOM5pmv55qE5Lq655qE5Yqg5YWl77yMUuivreiogOeahOekvuWMuuWcqOi/hemAn+aJqeWkp+aIkOmVv+OAgueOsOWcqOW3suS4jeS7heS7heaYr+e7n+iuoemihuWfn++8jOaVmeiCsu+8jOmTtuihjO+8jOeUteWVhu+8jOS6kuiBlOe9keKApi7pg73lnKjkvb/nlKhS6K+t6KiA44CCJw0KDQojIOiuvue9ruWuveW6puS4ujQw5Liq5a2X56ymDQpjYXQoc3RyX3dyYXAodHh0LCB3aWR0aCA9IDQwKSwgIlxuIikNCiMgUuivreiogOS9nOS4uue7n+iuoeWtpuS4gOmXqOivreiogO+8jOS4gOebtOWcqOWwj+S8l+mihuWfnw0KIyDpl6rogIDnnYDlhYnoipLjgILnm7TliLDlpKfmlbDmja7nmoTniIblj5HvvIxS6K+t6KiA5Y+Y5oiQDQojIOS6huS4gOmXqOeCmeaJi+WPr+eDreeahOaVsOaNruWIhuaekOeahOWIqeWZqOOAgumaj+edgOi2iuadpQ0KIyDotorlpJrnmoTlt6XnqIvog4zmma/nmoTkurrnmoTliqDlhaXvvIxS6K+t6KiA55qE56S+5Yy65ZyoDQojIOi/hemAn+aJqeWkp+aIkOmVv+OAgueOsOWcqOW3suS4jeS7heS7heaYr+e7n+iuoemihuWfn++8jOaVmQ0KIyDogrLvvIzpk7booYzvvIznlLXllYbvvIzkupLogZTnvZHigKYu6YO95Zyo5L2/55SoUuivreiogOOAgiANCg0KIyDorr7nva7lrr3luqbkuLo2MOWtl+espu+8jOmmluihjOe8qei/mzLlrZfnrKYNCmNhdChzdHJfd3JhcCh0eHQsIHdpZHRoID0gNjAsIGluZGVudCA9IDIpLCAiXG4iKQ0KIyAgIFLor63oqIDkvZzkuLrnu5/orqHlrabkuIDpl6jor63oqIDvvIzkuIDnm7TlnKjlsI/kvJfpoobln5/pl6rogIDnnYDlhYnoipLjgILnm7TliLDlpKfmlbANCiMg5o2u55qE54iG5Y+R77yMUuivreiogOWPmOaIkOS6huS4gOmXqOeCmeaJi+WPr+eDreeahOaVsOaNruWIhuaekOeahOWIqeWZqOOAgumaj+edgOi2iuadpQ0KIyDotorlpJrnmoTlt6XnqIvog4zmma/nmoTkurrnmoTliqDlhaXvvIxS6K+t6KiA55qE56S+5Yy65Zyo6L+F6YCf5omp5aSn5oiQ6ZW/44CC546w5Zyo5beyDQojIOS4jeS7heS7heaYr+e7n+iuoemihuWfn++8jOaVmeiCsu+8jOmTtuihjO+8jOeUteWVhu+8jOS6kuiBlOe9keKApi7pg73lnKjkvb/nlKhS6K+t6KiA44CCIA0KDQojIOiuvue9ruWuveW6puS4ujEw5a2X56ym77yM6Z2e6aaW6KGM57yp6L+bNOWtl+espg0KY2F0KHN0cl93cmFwKHR4dCwgd2lkdGggPSAxMCwgZXhkZW50ID0gNCksICJcbiIpDQojIFLor63oqIDkvZzkuLoNCiMgICAgIOe7n+iuoeWtpuS4gA0KIyAgICAg6Zeo6K+t6KiA77yMDQojICAgICDkuIDnm7TlnKjlsI8NCiMgICAgIOS8l+mihuWfn+mXqg0KIyAgICAg6ICA552A5YWJ6IqS44CCDQojICAgICDnm7TliLDlpKfmlbDmja4NCiMgICAgIOeahOeIhuWPke+8jFINCiMgICAgIOivreiogOWPmOaIkOS6hg0KIyAgICAg5LiA6Zeo54KZ5omL5Y+vDQojICAgICDng63nmoTmlbDmja7liIYNCiMgICAgIOaekOeahOWIqeWZqOOAgg0KIyAgICAg6ZqP552A6LaK5p2lDQojICAgICDotorlpJrnmoTlt6XnqIsNCiMgICAgIOiDjOaZr+eahOS6uueahA0KIyAgICAg5Yqg5YWl77yMUuivrQ0KIyAgICAg6KiA55qE56S+5Yy65ZyoDQojICAgICDov4XpgJ/mianlpKfmiJANCiMgICAgIOmVv+OAgueOsOWcqOW3sg0KIyAgICAg5LiN5LuF5LuF5piv57ufDQojICAgICDorqHpoobln5/vvIzmlZkNCiMgICAgIOiCsu+8jOmTtuihjO+8jA0KIyAgICAg55S15ZWG77yM5LqS6IGUDQojICAgICDnvZHigKYu6YO95Zyo5L2/DQojICAgICDnlKhS6K+t6KiA44CCDQoNCiMgTW9yZSBFeGFtcGxlcw0KdGhhbmtzX3BhdGggPC0gZmlsZS5wYXRoKFIuaG9tZSgiZG9jIiksICJUSEFOS1MiKQ0KdGhhbmtzIDwtIHN0cl9jKHJlYWRMaW5lcyh0aGFua3NfcGF0aCksIGNvbGxhcHNlID0gIlxuIikNCnRoYW5rcyA8LSB3b3JkKHRoYW5rcywgMSwgMywgZml4ZWQoIlxuXG4iKSkNCmNhdChzdHJfd3JhcCh0aGFua3MpLCAiXG4iKQ0KY2F0KHN0cl93cmFwKHRoYW5rcywgd2lkdGggPSA0MCksICJcbiIpDQpjYXQoc3RyX3dyYXAodGhhbmtzLCB3aWR0aCA9IDYwLCBpbmRlbnQgPSAyKSwgIlxuIikNCmNhdChzdHJfd3JhcCh0aGFua3MsIHdpZHRoID0gNjAsIGV4ZGVudCA9IDIpLCAiXG4iKQ0KIyBvbmUgd29yZCBvbmUgcm93DQpjYXQoc3RyX3dyYXAodGhhbmtzLCB3aWR0aCA9IDAsIGV4ZGVudCA9IDIpLCAiXG4iKQ0KYGBgDQoNCiMjIyMgMy4xLjYgc3RyX3N1YizmiKrlj5blrZfnrKbkuLINCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfc3ViKHN0cmluZywgc3RhcnQgPSAxTCwgZW5kID0gLTFMKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgc3RyaW5nYDog5a2X56ym5Liy77yM5a2X56ym5Liy5ZCR6YeP44CCDQoqIGBzdGFydGAgOiDlvIDlp4vkvY3nva4NCiogYGVuZGAgOiDnu5PmnZ/kvY3nva4NCg0K5oiq5Y+W5a2X56ym5Liy44CCDQoNCmBgYHtyIHN0cl9zdWIgZXgxfQ0KdHh0IDwtICJJIGFtIENvbmFuLiINCg0KIyDmiKrlj5YxLTTnmoTntKLlvJXkvY3nva7nmoTlrZfnrKbkuLINCnN0cl9zdWIodHh0LCAxLCA0KQ0KIyBbMV0gIkkgYW0iDQoNCiMg5oiq5Y+WMS0255qE57Si5byV5L2N572u55qE5a2X56ym5LiyDQpzdHJfc3ViKHR4dCwgZW5kPTYpDQojIFsxXSAiSSBhbSBDIg0KDQojIOaIquWPljbliLDnu5PmnZ/nmoTntKLlvJXkvY3nva7nmoTlrZfnrKbkuLIsZGVmYXVsdCB0byBlbmQgbGVuZ3RoKHR4dCkNCnN0cl9zdWIodHh0LCA2KQ0KIyBbMV0gIkNvbmFuLiINCg0KIyDliIYy5q615oiq5Y+W5a2X56ym5LiyDQpzdHJfc3ViKHR4dCwgYygxLCA0KSwgYyg2LCA4KSkNCiMgWzFdICJJIGFtIEMiICJtIENvbiIgDQoNCiMg6YCa6L+H6LSf5Z2Q5qCH5oiq5Y+W5a2X56ym5LiyIE5lZ2F0aXZlIGluZGljZXMNCnN0cl9zdWIodHh0LCAtMykNCiMgWzFdICJhbi4iDQpzdHJfc3ViKHR4dCwgZW5kID0gLTMpDQojIFsxXSAiSSBhbSBDb25hIg0KDQojIE1vcmUgRXhhbXBsZSBGcm9tIHN0cl9zdWIgaGVscA0KaHcgPC0gIkhhZGxleSBXaWNraGFtIg0KDQpzdHJfc3ViKGh3LCAxLCA2KQ0KIyBbMV0gIkhhZGxleSINCnN0cl9zdWIoaHcsIGVuZCA9IDYpDQojIFsxXSAiSGFkbGV5Ig0Kc3RyX3N1YihodywgOCwgMTQpDQojIFsxXSAiV2lja2hhbSINCnN0cl9zdWIoaHcsIDgpDQojIFsxXSAiV2lja2hhbSINCnN0cl9zdWIoaHcsIGMoMSwgOCksIGMoNiwgMTQpKQ0KIyBzdHJfc3ViKGh3LCBjKDEsIDgpLCBjKDYsIDE0KSkNCg0KIyBOZWdhdGl2ZSBpbmRpY2VzDQpzdHJfc3ViKGh3LCAtMSkNCiMgWzFdICJtIg0Kc3RyX3N1YihodywgLTcpDQojIFsxXSAiV2lja2hhbSINCnN0cl9zdWIoaHcsIGVuZCA9IC03KQ0KIyBbMV0gIkhhZGxleSBXIg0KDQojIEFsdGVybmF0aXZlbHksIHlvdSBjYW4gcGFzcyBpbiBhIHR3byBjb2x1bSBtYXRyaXgsIGFzIGluIHRoZQ0KIyBvdXRwdXQgZnJvbSBzdHJfbG9jYXRlX2FsbA0KcG9zIDwtIHN0cl9sb2NhdGVfYWxsKGh3LCAiW2FlaW9dIilbWzFdXQ0Kc3RyX3N1YihodywgcG9zKQ0Kc3RyX3N1YihodywgcG9zWywgMV0sIHBvc1ssIDJdKQ0KDQojIFZlY3RvcmlzYXRpb24NCnN0cl9zdWIoaHcsIHNlcV9sZW4oc3RyX2xlbmd0aChodykpKQ0KIyAgWzFdICJIYWRsZXkgV2lja2hhbSIgImFkbGV5IFdpY2toYW0iIA0KIyAgWzNdICJkbGV5IFdpY2toYW0iICAgImxleSBXaWNraGFtIiAgIA0KIyAgWzVdICJleSBXaWNraGFtIiAgICAgInkgV2lja2hhbSIgICAgIA0KIyAgWzddICIgV2lja2hhbSIgICAgICAgIldpY2toYW0iICAgICAgIA0KIyAgWzldICJpY2toYW0iICAgICAgICAgImNraGFtIiAgICAgICAgIA0KIyBbMTFdICJraGFtIiAgICAgICAgICAgImhhbSIgICAgICAgICAgIA0KIyBbMTNdICJhbSIgICAgICAgICAgICAgIm0iIA0Kc3RyX3N1YihodywgZW5kID0gc2VxX2xlbihzdHJfbGVuZ3RoKGh3KSkpDQojICBbMV0gIkgiICAgICAgICAgICAgICAiSGEiICAgICAgICAgICAgDQojICBbM10gIkhhZCIgICAgICAgICAgICAiSGFkbCIgICAgICAgICAgDQojICBbNV0gIkhhZGxlIiAgICAgICAgICAiSGFkbGV5IiAgICAgICAgDQojICBbN10gIkhhZGxleSAiICAgICAgICAiSGFkbGV5IFciICAgICAgDQojICBbOV0gIkhhZGxleSBXaSIgICAgICAiSGFkbGV5IFdpYyIgICAgDQojIFsxMV0gIkhhZGxleSBXaWNrIiAgICAiSGFkbGV5IFdpY2toIiAgDQojIFsxM10gIkhhZGxleSBXaWNraGEiICAiSGFkbGV5IFdpY2toYW0iDQoNCiMgUmVwbGFjZW1lbnQgZm9ybQ0KeCA8LSAiQkJDREVGIg0Kc3RyX3N1Yih4LCAxLCAxKSA8LSAiQSI7IHgNCiMgWzFdICJBQkNERUYiDQpzdHJfc3ViKHgsIC0xLCAtMSkgPC0gIksiOyB4DQojIFsxXSAiQUJDREVLIg0KDQojIHJlcGxhY2UgRSBpbiBBQkNERUsgd2l0aCBHSElKDQpzdHJfc3ViKHgsIC0yLCAtMikgPC0gIkdISUoiOyB4DQojIFsxXSAiQUJDREdISUpLIg0KDQojIHJlcGxhY2UgQkNER0hJSiBpbiBBQkNER0hJSksgd2l0aCBub24NCnN0cl9zdWIoeCwgMiwgLTIpIDwtICIiOyB4DQpgYGANCg0K5a+55oiq5Y+W55qE5a2X56ym5Liy6L+b6KGM6LWL5YC844CCRXh0cmFjdCBhbmQgcmVwbGFjZSBzdWJzdHJpbmdzIGZyb20gYSBjaGFyYWN0ZXIgdmVjdG9yLg0KDQpgYGB7ciBzdHJfc3ViIGV4Mn0NCnggPC0gIkFBQUJCQkNDQyINCg0KIyDlnKjlrZfnrKbkuLLnmoQx55qE5L2N572u6LWL5YC85Li6MQ0Kc3RyX3N1Yih4LCAxLCAxKSA8LSAxOyB4DQojIFsxXSAiMUFBQkJCQ0NDIg0KDQojIOWcqOWtl+espuS4suS7jjLliLAtMueahOS9jee9rui1i+WAvOS4ujIzNDUNCnN0cl9zdWIoeCwgMiwgLTIpIDwtICIyMzQ1IjsgeA0KIyBbMV0gIjEyMzQ1QyINCmBgYA0KDQojIyMgMy4yIOWtl+espuS4suiuoeeul+WHveaVsA0KDQoqIGBzdHJfY291bnRgOgnlrZfnrKbkuLLorqHmlbANCiogYHN0cl9sZW5ndGhgOgnlrZfnrKbkuLLplb/luqYNCiogYHN0cl9zb3J0YDoJ5a2X56ym5Liy5YC85o6S5bqPDQoqIGBzdHJfb3JkZXJgOgnlrZfnrKbkuLLntKLlvJXmjpLluo/vvIzop4TliJnlkIxgc3RyX3NvcnRgDQoNCiMjIyMgMy4yLjEgc3RyX2NvdW50LCDlrZfnrKbkuLLorqHmlbANCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfY291bnQoc3RyaW5nLCBwYXR0ZXJuID0gIiIpYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHBhdHRlcm5gOiDljLnphY3nmoTlrZfnrKbjgIINCg0K5a+55a2X56ym5Liy5Lit5Yy56YWN55qE5a2X56ym6K6h5pWwDQoNCmBgYHtyIHN0cl9jb3VudCBleDF9DQojIENvdW50IHRoZSBudW1iZXIgb2YgbWF0Y2hlcyBpbiBhIHN0cmluZy4NCnN0cl9jb3VudCgnYWFhNDQ0c3NzZGRkJywgImEiKQ0KIyBbMV0gMw0KYGBgDQoNCuWvueWtl+espuS4suWQkemHj+S4reWMuemFjeeahOWtl+espuiuoeaVsA0KYGBge3Igc3RyX2NvdW50IGV4Mn0NCmZydWl0IDwtIGMoImFwcGxlIiwgImJhbmFuYSIsICJwZWFyIiwgInBpbmVhcHBsZSIpDQpzdHJfY291bnQoZnJ1aXQsICJhIikNCiMgWzFdIDEgMyAxIDENCnN0cl9jb3VudChmcnVpdCwgInAiKQ0KIyBbMV0gMiAwIDEgMw0Kc3RyX2NvdW50KGZydWl0LCAiZSIpDQojIFsxXSAxIDAgMSAyDQoNCiMgbWF0Y2hlcyAxLTENCnN0cl9jb3VudChmcnVpdCwgYygiYSIsICJiIiwgInAiLCAicCIpKQ0KIyBbMV0gMSAxIDEgMw0KDQpgYGANCg0K5a+55a2X56ym5Liy5Lit55qEJy4n5a2X56ym6K6h5pWw77yM55Sx5LqOLuaYr+ato+WImeihqOi+vuW8j+eahOWMuemFjeespu+8jOebtOaOpeWIpOaWreiuoeaVsOeahOe7k+aenOaYr+S4jeWvueeahOOAgg0KDQpgYGB7ciBzdHJfY291bnQgZXgzfQ0Kc3RyX2NvdW50KGMoImEuIiwgIi4iLCAiLmEuIixOQSksICIuIikNCiMgWzFdICAyICAxICAzIE5BDQoNCiMg55SoZml4ZWTljLnphY3lrZfnrKYNCnN0cl9jb3VudChjKCJhLiIsICIuIiwgIi5hLiIsTkEpLCBmaXhlZCgiLiIpKQ0KIyBbMV0gIDEgIDEgIDIgTkENCg0KIyDnlKhcXOWMuemFjeWtl+espg0Kc3RyX2NvdW50KGMoImEuIiwgIi4iLCAiLmEuIixOQSksICJcXC4iKQ0KIyBbMV0gIDEgIDEgIDIgTkENCg0KYGBgDQoNCiMjIyMgMy4yLjIgc3RyX2xlbmd0aCzlrZfnrKbkuLLplb/luqYNCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfbGVuZ3RoKHN0cmluZylgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KDQrorqHnrpflrZfnrKbkuLLnmoTplb/luqYsVGhlIGxlbmd0aCBvZiBhIHN0cmluZyA6DQoNCmBgYHtyIHN0cl9sZW5ndGggZXgxfQ0Kc3RyX2xlbmd0aChjKCJJIiwgImFtIiwgIuW8oOS4uSIsIE5BKSkNCiMgWzFdICAxICAyICAyIE5BDQoNCiMgTW9yZSBFeGFtcGxlcw0KDQpzdHJfbGVuZ3RoKGxldHRlcnMpDQojIFsxXSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxDQojIFsyNl0gMQ0Kc3RyX2xlbmd0aChOQSkNCiMgWzFdIE5BDQpzdHJfbGVuZ3RoKGZhY3RvcigiYWJjIikpDQojIFsxXSAzDQpzdHJfbGVuZ3RoKGMoImkiLCAibGlrZSIsICJwcm9ncmFtbWluZyIsIE5BKSkNCiMgWzFdICAxICA0IDExIE5BDQoNCiMgVHdvIHdheXMgb2YgcmVwcmVzZW50aW5nIGEgdSB3aXRoIGFuIHVtbGF1dA0KdTEgPC0gIlx1MDBmYyINCnUyIDwtIHN0cmluZ2k6OnN0cmlfdHJhbnNfbmZkKHUxKQ0KIyBUaGUgcHJpbnQgdGhlIHNhbWU6DQp1MQ0KdTINCiMgQnV0IGhhdmUgYSBkaWZmZXJlbnQgbGVuZ3RoDQpzdHJfbGVuZ3RoKHUxKQ0KIyBbMV0gMQ0Kc3RyX2xlbmd0aCh1MikNCiMgWzFdIDINCg0KIyBFdmVuIHRob3VnaCB0aGV5IGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIGNoYXJhY3RlcnMNCnN0cl9jb3VudCh1MSkNCiMgWzFdIDENCnN0cl9jb3VudCh1MikNCiMgWzFdIDENCmBgYA0KDQojIyMjIDMuMi4zIHN0cl9zb3J0LCDlrZfnrKbkuLLlgLzmjpLluo/vvIzlkIxzdHJfb3JkZXLntKLlvJXmjpLluo8NCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfc29ydCh4LCBkZWNyZWFzaW5nID0gRkFMU0UsIG5hX2xhc3QgPSBUUlVFLCBsb2NhbGUgPSAiIiwgLi4uKWANCg0KYHN0cl9vcmRlcih4LCBkZWNyZWFzaW5nID0gRkFMU0UsIG5hX2xhc3QgPSBUUlVFLCBsb2NhbGUgPSAiIiwgLi4uKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgeGA6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgZGVjcmVhc2luZ2A6IOaOkuW6j+aWueWQkeOAgg0KKiBgbmFfbGFzdGA6YE5BYOWAvOeahOWtmOaUvuS9jee9ru+8jOS4gOWFsTPkuKrlgLzvvIxgVFJVRWDmlL7liLDmnIDlkI7vvIxgRkFMU0Vg5pS+5Yiw5pyA5YmN77yMYE5BYOi/h+a7pOWkhOeQhg0KKiBgbG9jYWxlYDrmjInlk6rnp43or63oqIDkuaDmg6/mjpLluo8NCg0K5a+55a2X56ym5Liy5YC86L+b6KGM5o6S5bqP44CCT3JkZXIgb3Igc29ydCBhIGNoYXJhY3RlciB2ZWN0b3IuDQoNCmBgYHtyIHN0cl9zb3J0IGV4MX0NCiMg5oyJQVNDSUnlrZfmr43mjpLluo8NCnN0cl9zb3J0KGMoJ2EnLDEsMiwnMTEnKSwgbG9jYWxlID0gImVuIikgIA0KIyBbMV0gIjEiICAiMTEiICIyIiAgImEiIA0KDQojIOWAkuW6j+aOkuW6jw0Kc3RyX3NvcnQobGV0dGVycyxkZWNyZWFzaW5nPVRSVUUpDQojICBbMV0gInoiICJ5IiAieCIgInciICJ2IiAidSIgInQiICJzIiAiciIgInEiICJwIiAibyIgIm4iICJtIiAibCIgImsiICJqIiAiaSIgImgiDQojIFsyMF0gImciICJmIiAiZSIgImQiICJjIiAiYiIgImEiDQoNCiMg5oyJ5ou86Z+z5o6S5bqPDQpzdHJfc29ydChjKCfkvaAnLCflpb0nLCfnsoknLCfkuJ0nLCfml6UnLCflv5cnKSxsb2NhbGUgPSAiemgiKSAgDQojIFsxXSAi57KJIiAi5aW9IiAi5L2gIiAi5pelIiAi5LidIiAi5b+XIg0KDQojIE1vcmUgZXhhbXBsZXMNCnN0cl9vcmRlcihsZXR0ZXJzKQ0Kc3RyX3NvcnQobGV0dGVycykNCnN0cl9vcmRlcihsZXR0ZXJzLCBsb2NhbGUgPSAiaGF3IikNCnN0cl9zb3J0KGxldHRlcnMsIGxvY2FsZSA9ICJoYXciKQ0KDQp4IDwtIGMoIjEwMGExMCIsICIxMDBhNSIsICIyYiIsICIyYSIpDQpzdHJfc29ydCh4KQ0Kc3RyX3NvcnQoeCwgbnVtZXJpYyA9IFRSVUUpDQoNCmBgYA0KDQrlr7lOQeWAvOeahOaOkuW6j+WkhOeQhg0KDQpgYGB7ciBzdHJfc29ydCBleDJ9DQoj5oqKTkHmlL7mnIDlkI7pnaINCnN0cl9zb3J0KGMoTkEsJzEnLE5BKSxuYV9sYXN0PVRSVUUpIA0KIyBbMV0gIjEiIE5BICBOQQ0KIA0KI+aKik5B5pS+5pyA5YmN6Z2iDQpzdHJfc29ydChjKE5BLCcxJyxOQSksbmFfbGFzdD1GQUxTRSkgDQojIFsxXSBOQSAgTkEgICIxIg0KDQoj5Y675o6JTkHlgLwgDQpzdHJfc29ydChjKE5BLCcxJyxOQSksbmFfbGFzdD1OQSkgICAgDQojIFsxXSAiMSINCmBgYA0KDQojIyMgMy4zIOWtl+espuS4suWMuemFjeWHveaVsA0KDQoqIGBzdHJfc3BsaXRgOgnlrZfnrKbkuLLliIblibINCiogYHN0cl9zcGxpdF9maXhlZGA6IOWtl+espuS4suWIhuWJsu+8jOWQjGBzdHJfc3BsaXRgDQoqIGBzdHJfc3Vic2V0YDog6L+U5Zue5Yy56YWN55qE5a2X56ym5LiyDQoqIGB3b3JkYDoJ5LuO5paH5pys5Lit5o+Q5Y+W5Y2V6K+NDQoqIGBzdHJfZGV0ZWN0YDog5qOA5p+l5Yy56YWN5a2X56ym5Liy55qE5a2X56ymDQoqIGBzdHJfbWF0Y2hgOgnku47lrZfnrKbkuLLkuK3mj5Dlj5bljLnphY3nu4TjgIINCiogYHN0cl9tYXRjaF9hbGxgOiDku47lrZfnrKbkuLLkuK3mj5Dlj5bljLnphY3nu4TvvIzlkIxgc3RyX21hdGNoYA0KKiBgc3RyX3JlcGxhY2VgOiDlrZfnrKbkuLLmm7/mjaINCiogYHN0cl9yZXBsYWNlX2FsbGA6IOWtl+espuS4suabv+aNou+8jOWQjGBzdHJfcmVwbGFjZWANCiogYHN0cl9yZXBsYWNlX25hYDrmiopOQeabv+aNouS4uk5B5a2X56ym5LiyDQoqIGBzdHJfbG9jYXRlYDog5om+5Yiw5Yy56YWN55qE5a2X56ym5Liy55qE5L2N572u44CCDQoqIGBzdHJfbG9jYXRlX2FsbGA6IOaJvuWIsOWMuemFjeeahOWtl+espuS4sueahOS9jee9rizlkIxgc3RyX2xvY2F0ZWANCiogYHN0cl9leHRyYWN0YDog5LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN5a2X56ymDQoqIGBzdHJfZXh0cmFjdF9hbGxgOiDku47lrZfnrKbkuLLkuK3mj5Dlj5bljLnphY3lrZfnrKbvvIzlkIxgc3RyX2V4dHJhY3RgDQoNCiMjIyMgMy4zLjEgc3RyX3NwbGl0LOWtl+espuS4suWIhuWJsu+8jOWQjHN0cl9zcGxpdF9maXhlZA0KDQoqKuWHveaVsOWumuS5iS1EZXNjcmlwdGlvbiAmIFVzYWdlKirvvJoNCg0KYHN0cl9zcGxpdChzdHJpbmcsIHBhdHRlcm4sIG4gPSBJbmYpYA0KYHN0cl9zcGxpdF9maXhlZChzdHJpbmcsIHBhdHRlcm4sIG4pYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHBhdHRlcm5gOiDljLnphY3nmoTlrZfnrKbjgIINCiogYG5gOiDliIblibLkuKrmlbANCg0K5a+55a2X56ym5Liy6L+b6KGM5YiG5Ymy44CCU3BsaXQgdXAgYSBzdHJpbmcgaW50byBwaWVjZXMuDQoNCmBgYHtyIHN0cl9zcGxpdCBleDF9DQp2YWwgPC0gImFiYywxMjMsMjM0LGl1dXUiDQoNCiMg5LulLOi/m+ihjOWIhuWJsg0KczE8LXN0cl9zcGxpdCh2YWwsICIsIik7czENCiMgW1sxXV0NCiMgWzFdICJhYmMiICAiMTIzIiAgIjIzNCIgICJpdXV1Ig0KDQojIOS7pSzov5vooYzliIblibLvvIzkv53nlZky5Z2XDQpzMjwtc3RyX3NwbGl0KHZhbCwgIiwiLDIpO3MyDQojIFtbMV1dDQojIFsxXSAiYWJjIiAgICAgICAgICAiMTIzLDIzNCxpdXV1Ig0KDQojIOafpeeci3N0cl9zcGxpdCgp5Ye95pWw5pON5L2c55qE57uT5p6c57G75Z6LbGlzdA0KY2xhc3MoczEpDQojIFsxXSAibGlzdCINCg0KIyDnlKhzdHJfc3BsaXRfZml4ZWQoKeWHveaVsOWIhuWJsu+8jOe7k+aenOexu+Wei+aYr21hdHJpeA0KczM8LXN0cl9zcGxpdF9maXhlZCh2YWwsICIsIiwyKTtzMw0KICAgICAjIFssMV0gIFssMl0gICAgICAgICAgDQojIFsxLF0gImFiYyIgIjEyMywyMzQsaXV1dSINCg0KY2xhc3MoczMpDQojIFsxXSAibWF0cml4Ig0KDQojIG1vcmUgZXhhbXBsZXMNCmZydWl0cyA8LSBjKA0KICAiYXBwbGVzIGFuZCBvcmFuZ2VzIGFuZCBwZWFycyBhbmQgYmFuYW5hcyIsDQogICJwaW5lYXBwbGVzIGFuZCBtYW5nb3MgYW5kIGd1YXZhcyINCikNCg0Kc3RyX3NwbGl0KGZydWl0cywgIiBhbmQgIikNCiMgW1sxXV0NCiMgWzFdICJhcHBsZXMiICAib3JhbmdlcyIgInBlYXJzIiAgICJiYW5hbmFzIg0KIyANCiMgW1syXV0NCiMgWzFdICJwaW5lYXBwbGVzIiAibWFuZ29zIiAgICAgImd1YXZhcyIgIA0KDQpzdHJfc3BsaXQoZnJ1aXRzLCAiIGFuZCAiLCBzaW1wbGlmeSA9IFRSVUUpDQojICAgICAgWywxXSAgICAgICAgIFssMl0gICAgICBbLDNdICAgICBbLDRdICAgICANCiMgWzEsXSAiYXBwbGVzIiAgICAgIm9yYW5nZXMiICJwZWFycyIgICJiYW5hbmFzIg0KIyBbMixdICJwaW5lYXBwbGVzIiAibWFuZ29zIiAgImd1YXZhcyIgIiIgDQoNCiMgU3BlY2lmeSBuIHRvIHJlc3RyaWN0IHRoZSBudW1iZXIgb2YgcG9zc2libGUgbWF0Y2hlcw0Kc3RyX3NwbGl0KGZydWl0cywgIiBhbmQgIiwgbiA9IDMpDQojIFtbMV1dDQojIFsxXSAiYXBwbGVzIiAgICAgICAgICAgICJvcmFuZ2VzIiAgICAgICAgICANCiMgWzNdICJwZWFycyBhbmQgYmFuYW5hcyINCiMgDQojIFtbMl1dDQojIFsxXSAicGluZWFwcGxlcyIgIm1hbmdvcyIgICAgICJndWF2YXMiIA0KDQpzdHJfc3BsaXQoZnJ1aXRzLCAiIGFuZCAiLCBuID0gMikNCiMgW1sxXV0NCiMgWzFdICJhcHBsZXMiICAgICAgICAgICAgICAgICAgICAgICANCiMgWzJdICJvcmFuZ2VzIGFuZCBwZWFycyBhbmQgYmFuYW5hcyINCiMgDQojIFtbMl1dDQojIFsxXSAicGluZWFwcGxlcyIgICAgICAgICJtYW5nb3MgYW5kIGd1YXZhcyINCg0KIyBJZiBuIGdyZWF0ZXIgdGhhbiBudW1iZXIgb2YgcGllY2VzLCBubyBwYWRkaW5nIG9jY3Vycw0Kc3RyX3NwbGl0KGZydWl0cywgIiBhbmQgIiwgbiA9IDUpDQojIFtbMV1dDQojIFsxXSAiYXBwbGVzIiAgIm9yYW5nZXMiICJwZWFycyIgICAiYmFuYW5hcyINCiMgDQojIFtbMl1dDQojIFsxXSAicGluZWFwcGxlcyIgIm1hbmdvcyIgICAgICJndWF2YXMiDQoNCiMgVXNlIGZpeGVkIHRvIHJldHVybiBhIGNoYXJhY3RlciBtYXRyaXgNCnN0cl9zcGxpdF9maXhlZChmcnVpdHMsICIgYW5kICIsIDMpDQojICAgICAgWywxXSAgICAgICAgIFssMl0gICAgICBbLDNdICAgICAgICAgICAgICAgDQojIFsxLF0gImFwcGxlcyIgICAgICJvcmFuZ2VzIiAicGVhcnMgYW5kIGJhbmFuYXMiDQojIFsyLF0gInBpbmVhcHBsZXMiICJtYW5nb3MiICAiZ3VhdmFzIiANCg0Kc3RyX3NwbGl0X2ZpeGVkKGZydWl0cywgIiBhbmQgIiwgNCkNCiMgICAgICBbLDFdICAgICAgICAgWywyXSAgICAgIFssM10gICAgIFssNF0gICAgIA0KIyBbMSxdICJhcHBsZXMiICAgICAib3JhbmdlcyIgInBlYXJzIiAgImJhbmFuYXMiDQojIFsyLF0gInBpbmVhcHBsZXMiICJtYW5nb3MiICAiZ3VhdmFzIiAiIiANCmBgYA0KDQojIyMjIDMuMy4yIHN0cl9zdWJzZXQ66L+U5Zue55qE5Yy56YWN5a2X56ym5LiyDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX3N1YnNldChzdHJpbmcsIHBhdHRlcm4pYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHBhdHRlcm5gOiDljLnphY3nmoTlrZfnrKbjgIINCg0KYGBge3Igc3RyX3N1YnNldCBleDF9DQp2YWwgPC0gYygiYWJjIiwgMTIzLCAiY2JhIikNCg0KIyDlhajmlofljLnphY0NCnN0cl9zdWJzZXQodmFsLCAiYSIpDQojIFsxXSAiYWJjIiAiY2JhIg0KDQojIOW8gOWktOWMuemFjQ0Kc3RyX3N1YnNldCh2YWwsICJeYSIpDQojIFsxXSAiYWJjIg0KDQojIOe7k+WwvuWMuemFjQ0Kc3RyX3N1YnNldCh2YWwsICJhJCIpDQojIFsxXSAiY2JhIg0KYGBgDQoNCiMjIyMgMy4zLjMgd29yZCwg5LuO5paH5pys5Lit5o+Q5Y+W5Y2V6K+NDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgd29yZChzdHJpbmcsIHN0YXJ0ID0gMUwsIGVuZCA9IHN0YXJ0LCBzZXAgPSBmaXhlZCgiICIpKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgc3RyaW5nYDog5a2X56ym5Liy77yM5a2X56ym5Liy5ZCR6YeP44CCDQoqIGBzdGFydGA6IOW8gOWni+S9jee9ruOAgg0KKiBgZW5kYDog57uT5p2f5L2N572u44CCDQoqIGBzZXBgOiDljLnphY3lrZfnrKbjgIINCg0KRXh0cmFjdCB3b3JkcyBmcm9tIGEgc2VudGVuY2UuIA0KDQpgYGB7ciB3b3JkIGV4MX0NCnZhbCA8LSBjKCJJIGFtIENvbmFuLiIsICJodHRwOi8vZmVucy5tZSwgb2siKQ0KDQojIOm7mOiupOS7peepuuagvOWIhuWJsu+8jOWPluesrOS4gOS4quS9jee9rueahOWtl+espuS4sg0Kd29yZCh2YWwsIDEpDQojIFsxXSAiSSIgICAgICAgICAgICAgICAiaHR0cDovL2ZlbnMubWUsIg0Kd29yZCh2YWwsIC0xKQ0KIyBbMV0gIkNvbmFuLiIgIm9rIiAgICANCndvcmQodmFsLCAyLCAtMSkNCiMgWzFdICJhbSBDb25hbi4iICJvayIgICAgICAgDQoNCiMg5LulLOWIhuWJsu+8jOWPluesrOS4gOS4quS9jee9rueahOWtl+espuS4siANCnZhbDwtJzExMSwyMjIsMzMzLDQ0NCcNCndvcmQodmFsLCAxLCBzZXAgPSBmaXhlZCgnLCcpKQ0KIyBbMV0gIjExMSINCndvcmQodmFsLCAzLCBzZXAgPSBmaXhlZCgnLCcpKQ0KIyBbMV0gIjMzMyINCmBgYA0KDQoNCg0KIyMjIyAzLjMuNCBzdHJfZGV0ZWN05Yy56YWN5a2X56ym5Liy55qE5a2X56ymDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpzdHJfZGV0ZWN0KHN0cmluZywgcGF0dGVybikNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgcGF0dGVybmA6IOWMuemFjeWtl+espuOAgg0KDQpEZXRlY3QgdGhlIHByZXNlbmNlIG9yIGFic2VuY2Ugb2YgYSBwYXR0ZXJuIGluIGEgc3RyaW5nLg0KDQpgYGB7ciBzdHJfZGV0ZWN0IGV4MX0NCnZhbCA8LSBjKCJhYmNhNCIsIDEyMywgImNiYTIiKQ0KDQojIOajgOafpeWtl+espuS4suWQkemHj++8jOaYr+WQpuWMheaLrGENCnN0cl9kZXRlY3QodmFsLCAiYSIpDQojIFsxXSAgVFJVRSBGQUxTRSAgVFJVRQ0KDQojIOajgOafpeWtl+espuS4suWQkemHj++8jOaYr+WQpuS7pWHkuLrlvIDlpLQNCnN0cl9kZXRlY3QodmFsLCAiXmEiKQ0KIyBbMV0gIFRSVUUgRkFMU0UgRkFMU0UNCg0KIyDmo4Dmn6XlrZfnrKbkuLLlkJHph4/vvIzmmK/lkKbku6Vh5Li657uT5bC+DQpzdHJfZGV0ZWN0KHZhbCwgImEkIikNCiMgWzFdIEZBTFNFIEZBTFNFIEZBTFNFDQoNCiMgTW9yZSBFeGFtcGxlcw0KZnJ1aXQgPC0gYygiYXBwbGUiLCAiYmFuYW5hIiwgInBlYXIiLCAicGluYXBwbGUiKTtmcnVpdA0KIyBbMV0gImFwcGxlIiAgICAiYmFuYW5hIiAgICJwZWFyIiAgICAgInBpbmFwcGxlIg0Kc3RyX2RldGVjdChmcnVpdCwgImEiKQ0KIyBbMV0gVFJVRSBUUlVFIFRSVUUgVFJVRQ0Kc3RyX2RldGVjdChmcnVpdCwgIl5hIikNCiMgWzFdICBUUlVFIEZBTFNFIEZBTFNFIEZBTFNFDQpzdHJfZGV0ZWN0KGZydWl0LCAiYSQiKQ0KIyBbMV0gRkFMU0UgIFRSVUUgRkFMU0UgRkFMU0UNCnN0cl9kZXRlY3QoZnJ1aXQsICJiIikNCiMgWzFdIEZBTFNFICBUUlVFIEZBTFNFIEZBTFNFDQpzdHJfZGV0ZWN0KGZydWl0LCAiW2FlaW91XSIpDQojIEFsc28gdmVjdG9yaXNlZCBvdmVyIHBhdHRlcm4NCnN0cl9kZXRlY3QoImFlY2ZnIiwgbGV0dGVycykNCiMgIFsxXSAgVFJVRSBGQUxTRSAgVFJVRSBGQUxTRSAgVFJVRSAgVFJVRSAgVFJVRSBGQUxTRQ0KIyAgWzldIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFIEZBTFNFDQojIFsxN10gRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UgRkFMU0UNCiMgWzI1XSBGQUxTRSBGQUxTRQ0KDQpgYGANCg0KIyMjIyAzLjMuNiBzdHJfbWF0Y2gs5LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN57uEDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX21hdGNoKHN0cmluZywgcGF0dGVybilgDQpgc3RyX21hdGNoX2FsbChzdHJpbmcsIHBhdHRlcm4pYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHBhdHRlcm5gOiDljLnphY3lrZfnrKbjgIINCg0K5LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN57uERXh0cmFjdCBtYXRjaGVkIGdyb3VwcyBmcm9tIGEgc3RyaW5nLg0KDQpgYGB7ciBzdHJfbWF0Y2ggZXgxfQ0KdmFsIDwtIGMoImFiYyIsIDEyMywgImNiYSIpDQoNCiMg5Yy56YWN5a2X56ymYe+8jOW5tui/lOWbnuWvueW6lOeahOWtl+espg0Kc3RyX21hdGNoKHZhbCwgImEiKQ0KIyAgICAgIFssMV0NCiMgWzEsXSAiYSIgDQojIFsyLF0gTkEgIA0KIyBbMyxdICJhIiANCg0KIyDljLnphY3lrZfnrKYwLTnvvIzpmZAx5Liq77yM5bm26L+U5Zue5a+55bqU55qE5a2X56ymDQpzdHJfbWF0Y2godmFsLCAiWzAtOV0iKQ0KIyAgICAgIFssMV0NCiMgWzEsXSBOQSAgDQojIFsyLF0gIjEiIA0KIyBbMyxdIE5BICANCg0KIyDljLnphY3lrZfnrKYwLTnvvIzkuI3pmZDmlbDph4/vvIzlubbov5Tlm57lr7nlupTnmoTlrZfnrKYNCnN0cl9tYXRjaCh2YWwsICJbMC05XSoiKQ0KIyAgICAgIFssMV0gDQojIFsxLF0gIiIgICANCiMgWzIsXSAiMTIzIg0KIyBbMyxdICIiICANCg0Kc3RyaW5ncyA8LSBjKCIgMjE5IDczMyA4OTY1IiwgIjMyOS0yOTMtODc1MyAiLCAiYmFuYW5hIiwgIjU5NSA3OTQgNzU2OSIsDQogICIzODcgMjg3IDY3MTgiLCAiYXBwbGUiLCAiMjMzLjM5OC45MTg3ICAiLCAiNDgyIDk1MiAzMzE1IiwNCiAgIjIzOSA5MjMgODExNSBhbmQgODQyIDU2NiA0NjkyIiwgIldvcms6IDU3OS00OTktNzUyNyIsICIkMTAwMCIsDQogICJIb21lOiA1NDMuMzU1LjM2NzkiKTtzdHJpbmdzDQojIFsxXSAiIDIxOSA3MzMgODk2NSIgICAgICAgICAgICAgICAgDQojICBbMl0gIjMyOS0yOTMtODc1MyAiICAgICAgICAgICAgICAgIA0KIyAgWzNdICJiYW5hbmEiICAgICAgICAgICAgICAgICAgICAgICANCiMgIFs0XSAiNTk1IDc5NCA3NTY5IiAgICAgICAgICAgICAgICAgDQojICBbNV0gIjM4NyAyODcgNjcxOCIgICAgICAgICAgICAgICAgIA0KIyAgWzZdICJhcHBsZSIgICAgICAgICAgICAgICAgICAgICAgICANCiMgIFs3XSAiMjMzLjM5OC45MTg3ICAiICAgICAgICAgICAgICAgDQojICBbOF0gIjQ4MiA5NTIgMzMxNSIgICAgICAgICAgICAgICAgIA0KIyAgWzldICIyMzkgOTIzIDgxMTUgYW5kIDg0MiA1NjYgNDY5MiINCiMgWzEwXSAiV29yazogNTc5LTQ5OS03NTI3IiAgICAgICAgICAgDQojIFsxMV0gIiQxMDAwIiAgICAgICAgICAgICAgICAgICAgICAgIA0KIyBbMTJdICJIb21lOiA1NDMuMzU1LjM2NzkiIA0KcGhvbmUgPC0gIihbMi05XVswLTldezJ9KVstIC5dKFswLTldezN9KVstIC5dKFswLTldezR9KSI7cGhvbmUNCg0Kc3RyX2V4dHJhY3Qoc3RyaW5ncywgcGhvbmUpDQojIFsxXSAiMjE5IDczMyA4OTY1IiAiMzI5LTI5My04NzUzIiBOQSAgICAgICAgICAgIA0KIyAgWzRdICI1OTUgNzk0IDc1NjkiICIzODcgMjg3IDY3MTgiIE5BICAgICAgICAgICAgDQojICBbN10gIjIzMy4zOTguOTE4NyIgIjQ4MiA5NTIgMzMxNSIgIjIzOSA5MjMgODExNSINCiMgWzEwXSAiNTc5LTQ5OS03NTI3IiBOQSAgICAgICAgICAgICAiNTQzLjM1NS4zNjc5Ig0KDQpzdHJfbWF0Y2goc3RyaW5ncywgcGhvbmUpDQojICAgICBbLDFdICAgICAgICAgICBbLDJdICBbLDNdICBbLDRdICANCiMgIFsxLF0gIjIxOSA3MzMgODk2NSIgIjIxOSIgIjczMyIgIjg5NjUiDQojICBbMixdICIzMjktMjkzLTg3NTMiICIzMjkiICIyOTMiICI4NzUzIg0KIyAgWzMsXSBOQSAgICAgICAgICAgICBOQSAgICBOQSAgICBOQSAgICANCiMgIFs0LF0gIjU5NSA3OTQgNzU2OSIgIjU5NSIgIjc5NCIgIjc1NjkiDQojICBbNSxdICIzODcgMjg3IDY3MTgiICIzODciICIyODciICI2NzE4Ig0KIyAgWzYsXSBOQSAgICAgICAgICAgICBOQSAgICBOQSAgICBOQSAgICANCiMgIFs3LF0gIjIzMy4zOTguOTE4NyIgIjIzMyIgIjM5OCIgIjkxODciDQojICBbOCxdICI0ODIgOTUyIDMzMTUiICI0ODIiICI5NTIiICIzMzE1Ig0KIyAgWzksXSAiMjM5IDkyMyA4MTE1IiAiMjM5IiAiOTIzIiAiODExNSINCiMgWzEwLF0gIjU3OS00OTktNzUyNyIgIjU3OSIgIjQ5OSIgIjc1MjciDQojIFsxMSxdIE5BICAgICAgICAgICAgIE5BICAgIE5BICAgIE5BICAgIA0KIyBbMTIsXSAiNTQzLjM1NS4zNjc5IiAiNTQzIiAiMzU1IiAiMzY3OSINCg0KIyBFeHRyYWN0L21hdGNoIGFsbA0Kc3RyX2V4dHJhY3RfYWxsKHN0cmluZ3MsIHBob25lKQ0KIyBbWzFdXQ0KIyBbMV0gIjIxOSA3MzMgODk2NSINCiMgDQojIFtbMl1dDQojIFsxXSAiMzI5LTI5My04NzUzIg0KIyANCiMgW1szXV0NCiMgY2hhcmFjdGVyKDApDQojIA0KIyBbWzRdXQ0KIyBbMV0gIjU5NSA3OTQgNzU2OSINCiMgDQojIFtbNV1dDQojIFsxXSAiMzg3IDI4NyA2NzE4Ig0KIyANCiMgW1s2XV0NCiMgY2hhcmFjdGVyKDApDQojIA0KIyBbWzddXQ0KIyBbMV0gIjIzMy4zOTguOTE4NyINCiMgDQojIFtbOF1dDQojIFsxXSAiNDgyIDk1MiAzMzE1Ig0KIyANCiMgW1s5XV0NCiMgWzFdICIyMzkgOTIzIDgxMTUiICI4NDIgNTY2IDQ2OTIiDQojIA0KIyBbWzEwXV0NCiMgWzFdICI1NzktNDk5LTc1MjciDQojIA0KIyBbWzExXV0NCiMgY2hhcmFjdGVyKDApDQojIA0KIyBbWzEyXV0NCiMgWzFdICI1NDMuMzU1LjM2NzkiDQoNCnN0cl9tYXRjaF9hbGwoc3RyaW5ncywgcGhvbmUpDQojIFtbMV1dDQojICAgICAgWywxXSAgICAgICAgICAgWywyXSAgWywzXSAgWyw0XSAgDQojIFsxLF0gIjIxOSA3MzMgODk2NSIgIjIxOSIgIjczMyIgIjg5NjUiDQojIA0KIyBbWzJdXQ0KIyAgICAgIFssMV0gICAgICAgICAgIFssMl0gIFssM10gIFssNF0gIA0KIyBbMSxdICIzMjktMjkzLTg3NTMiICIzMjkiICIyOTMiICI4NzUzIg0KIyANCiMgW1szXV0NCiMgICAgICBbLDFdIFssMl0gWywzXSBbLDRdDQojIA0KIyBbWzRdXQ0KIyAgICAgIFssMV0gICAgICAgICAgIFssMl0gIFssM10gIFssNF0gIA0KIyBbMSxdICI1OTUgNzk0IDc1NjkiICI1OTUiICI3OTQiICI3NTY5Ig0KIyANCiMgW1s1XV0NCiMgICAgICBbLDFdICAgICAgICAgICBbLDJdICBbLDNdICBbLDRdICANCiMgWzEsXSAiMzg3IDI4NyA2NzE4IiAiMzg3IiAiMjg3IiAiNjcxOCINCiMgDQojIFtbNl1dDQojICAgICAgWywxXSBbLDJdIFssM10gWyw0XQ0KIyANCiMgW1s3XV0NCiMgICAgICBbLDFdICAgICAgICAgICBbLDJdICBbLDNdICBbLDRdICANCiMgWzEsXSAiMjMzLjM5OC45MTg3IiAiMjMzIiAiMzk4IiAiOTE4NyINCiMgDQojIFtbOF1dDQojICAgICAgWywxXSAgICAgICAgICAgWywyXSAgWywzXSAgWyw0XSAgDQojIFsxLF0gIjQ4MiA5NTIgMzMxNSIgIjQ4MiIgIjk1MiIgIjMzMTUiDQojIA0KIyBbWzldXQ0KIyAgICAgIFssMV0gICAgICAgICAgIFssMl0gIFssM10gIFssNF0gIA0KIyBbMSxdICIyMzkgOTIzIDgxMTUiICIyMzkiICI5MjMiICI4MTE1Ig0KIyBbMixdICI4NDIgNTY2IDQ2OTIiICI4NDIiICI1NjYiICI0NjkyIg0KIyANCiMgW1sxMF1dDQojICAgICAgWywxXSAgICAgICAgICAgWywyXSAgWywzXSAgWyw0XSAgDQojIFsxLF0gIjU3OS00OTktNzUyNyIgIjU3OSIgIjQ5OSIgIjc1MjciDQojIA0KIyBbWzExXV0NCiMgICAgICBbLDFdIFssMl0gWywzXSBbLDRdDQojIA0KIyBbWzEyXV0NCiMgICAgICBbLDFdICAgICAgICAgICBbLDJdICBbLDNdICBbLDRdICANCiMgWzEsXSAiNTQzLjM1NS4zNjc5IiAiNTQzIiAiMzU1IiAiMzY3OSINCg0KYGBgDQoNCuS7juWtl+espuS4suS4reaPkOWPluWMuemFjee7hO+8jOS7peWtl+espuS4sm1hdHJpeOagvOW8j+i/lOWbng0KDQpgYGB7ciBzdHJfbWF0Y2hfYWxsIGV4MX0NCg0Kc3RyX21hdGNoX2FsbCh2YWwsICJhIikNCiMgW1sxXV0NCiMgICAgICBbLDFdDQojIFsxLF0gImEiIA0KIyANCiMgW1syXV0NCiMgICAgICBbLDFdDQojIA0KIyBbWzNdXQ0KIyAgICAgIFssMV0NCiMgWzEsXSAiYSIgDQoNCnN0cl9tYXRjaF9hbGwodmFsLCAiWzAtOV0iKQ0KIyBbWzFdXQ0KIyAgICAgIFssMV0NCiMgDQojIFtbMl1dDQojICAgICAgWywxXQ0KIyBbMSxdICIxIiANCiMgWzIsXSAiMiIgDQojIFszLF0gIjMiIA0KIyANCiMgW1szXV0NCiMgICAgICBbLDFdDQoNCnggPC0gYygiPGE+IDxiPiIsICI8YT4gPD4iLCAiPGE+IiwgIiIsIE5BKTt4DQpzdHJfbWF0Y2goeCwgIjwoLio/KT4gPCguKj8pPiIpDQojICAgICAgWywxXSAgICAgIFssMl0gWywzXQ0KIyBbMSxdICI8YT4gPGI+IiAiYSIgICJiIiANCiMgWzIsXSAiPGE+IDw+IiAgImEiICAiIiAgDQojIFszLF0gTkEgICAgICAgIE5BICAgTkEgIA0KIyBbNCxdIE5BICAgICAgICBOQSAgIE5BICANCiMgWzUsXSBOQSAgICAgICAgTkEgICBOQSAgDQoNCnN0cl9tYXRjaF9hbGwoeCwgIjwoLio/KT4iKQ0KIyBbWzFdXQ0KIyAgICAgIFssMV0gIFssMl0NCiMgWzEsXSAiPGE+IiAiYSIgDQojIFsyLF0gIjxiPiIgImIiIA0KIyANCiMgW1syXV0NCiMgICAgICBbLDFdICBbLDJdDQojIFsxLF0gIjxhPiIgImEiIA0KIyBbMixdICI8PiIgICIiICANCiMgDQojIFtbM11dDQojICAgICAgWywxXSAgWywyXQ0KIyBbMSxdICI8YT4iICJhIiANCiMgDQojIFtbNF1dDQojICAgICAgWywxXSBbLDJdDQojIA0KIyBbWzVdXQ0KIyAgICAgIFssMV0gWywyXQ0KIyBbMSxdIE5BICAgTkEgIA0KDQpzdHJfZXh0cmFjdCh4LCAiPC4qPz4iKQ0KIyBbMV0gIjxhPiIgIjxhPiIgIjxhPiIgTkEgICAgTkEgICANCnN0cl9leHRyYWN0X2FsbCh4LCAiPC4qPz4iKQ0KIyBbWzFdXQ0KIyBbMV0gIjxhPiIgIjxiPiINCiMgDQojIFtbMl1dDQojIFsxXSAiPGE+IiAiPD4iIA0KIyANCiMgW1szXV0NCiMgWzFdICI8YT4iDQojIA0KIyBbWzRdXQ0KIyBjaGFyYWN0ZXIoMCkNCiMgDQojIFtbNV1dDQojIFsxXSBOQQ0KDQpgYGANCg0KDQojIyMjIDMuMy43IHN0cl9yZXBsYWNl77yM5a2X56ym5Liy5pu/5o2iDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX3JlcGxhY2Uoc3RyaW5nLCBwYXR0ZXJuLCByZXBsYWNlbWVudClgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4su+8jOWtl+espuS4suWQkemHj+OAgg0KKiBgcGF0dGVybmA6IOWMuemFjeWtl+espuOAgg0KKiBgcmVwbGFjZW1lbnRgOiDnlKjkuo7mm7/mjaLnmoTlrZfnrKbjgIINCg0KUmVwbGFjZSBtYXRjaGVkIHBhdHRlcm5zIGluIGEgc3RyaW5nLg0KDQpgYGB7ciBzdHJfcmVwbGFjZX0NCnZhbCA8LSBjKCJhYmMiLCAxMjMsICJjYmEiKQ0KDQojIOaKiuebruagh+Wtl+espuS4suesrOS4gOS4quWHuueOsOeahGHmiJZi77yM5pu/5o2i5Li6LQ0Kc3RyX3JlcGxhY2UodmFsLCAiW2FiXSIsICItIikNCiMgWzFdICItYmMiICIxMjMiICJjLWEiDQoNCiMg5oqK55uu5qCH5a2X56ym5Liy5omA5pyJ5Ye6546w55qEYeaIlmLvvIzmm7/mjaLkuLotDQpzdHJfcmVwbGFjZV9hbGwodmFsLCAiW2FiXSIsICItIikNCiMgWzFdICItLWMiICIxMjMiICJjLS0iDQoNCiMg5oqK55uu5qCH5a2X56ym5Liy5omA5pyJ5Ye6546w55qEYe+8jOabv+aNouS4uuiiq+i9rOS5ieeahOWtl+espg0Kc3RyX3JlcGxhY2VfYWxsKHZhbCwgIlthXSIsICJcMVwxIikNCiMgWzFdICJcMDAxXDAwMWJjIiAiMTIzIiAgICAgICAgImNiXDAwMVwwMDEiDQoNCiMjIE1vcmUgRXhhbXBsZXMNCg0KZnJ1aXRzIDwtIGMoIm9uZSBhcHBsZSIsICJ0d28gcGVhcnMiLCAidGhyZWUgYmFuYW5hcyIpO2ZydWl0cw0KIyBbMV0gIm9uZSBhcHBsZSIgICAgICJ0d28gcGVhcnMiICAgICAidGhyZWUgYmFuYW5hcyINCg0KIyByZXBsYWNlIHdpdGggdGhlIDFzdCBtYXRjaA0Kc3RyX3JlcGxhY2UoZnJ1aXRzLCAiW2FlaW91XSIsICItIikNCiMgWzFdICItbmUgYXBwbGUiICAgICAidHctIHBlYXJzIiAgICAgInRoci1lIGJhbmFuYXMiDQoNCiNyZXBsYWNlIGFsbA0Kc3RyX3JlcGxhY2VfYWxsKGZydWl0cywgIlthZWlvdV0iLCAiLSIpDQojIFsxXSAiLW4tIC1wcGwtIiAgICAgInR3LSBwLS1ycyIgICAgICJ0aHItLSBiLW4tbi1zIg0KDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCAiW2FlaW91XSIsIHRvdXBwZXIpDQojIFsxXSAiT25FIEFwcGxFIiAgICAgInR3TyBwRUFycyIgICAgICJ0aHJFRSBiQW5BbkFzIg0KDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCAiYiIsIE5BX2NoYXJhY3Rlcl8pDQojIFsxXSAib25lIGFwcGxlIiAidHdvIHBlYXJzIiBOQSAgDQoNCnN0cl9yZXBsYWNlKGZydWl0cywgIihbYWVpb3VdKSIsICIiKQ0KIyBbMV0gIm5lIGFwcGxlIiAgICAgInR3IHBlYXJzIiAgICAgInRocmUgYmFuYW5hcyINCg0Kc3RyX3JlcGxhY2UoZnJ1aXRzLCAiKFthZWlvdV0pIiwgIlxcMVxcMSIpDQojIFsxXSAib29uZSBhcHBsZSIgICAgICJ0d29vIHBlYXJzIiAgICAgInRocmVlZSBiYW5hbmFzIg0KDQpzdHJfcmVwbGFjZShmcnVpdHMsICJbYWVpb3VdIiwgYygiMSIsICIyIiwgIjMiKSkNCiMgWzFdICIxbmUgYXBwbGUiICAgICAidHcyIHBlYXJzIiAgICAgInRocjNlIGJhbmFuYXMiDQoNCnN0cl9yZXBsYWNlKGZydWl0cywgYygiYSIsICJlIiwgImkiKSwgIi0iKQ0KIyBbMV0gIm9uZSAtcHBsZSIgICAgICJ0d28gcC1hcnMiICAgICAidGhyZWUgYmFuYW5hcyINCg0KZnJ1aXRzIDwtIGMoIm9uZSBhcHBsZSIsICJ0d28gcGVhcnMiLCAidGhyZWUgYmFuYW5hcyIpDQpzdHJfcmVwbGFjZShmcnVpdHMsICJbYWVpb3VdIiwgIi0iKQ0KDQojIHN0cl9yZXBsYWNlX2FsbA0Kc3RyX3JlcGxhY2VfYWxsKGZydWl0cywgIlthZWlvdV0iLCAiLSIpDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCAiKFthZWlvdV0pIiwgIiIpDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCAiKFthZWlvdV0pIiwgIlxcMVxcMSIpDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCAiW2FlaW91XSIsIGMoIjEiLCAiMiIsICIzIikpDQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRzLCBjKCJhIiwgImUiLCAiaSIpLCAiLSIpDQoNCiMgSWYgeW91IHdhbnQgdG8gYXBwbHkgbXVsdGlwbGUgcGF0dGVybnMgYW5kIHJlcGxhY2VtZW50cyB0byB0aGUgc2FtZQ0KIyBzdHJpbmcsIHBhc3MgYSBuYW1lZCB2ZWN0b3IgdG8gcGF0dGVybi4NCmZydWl0cyAlPiUNCiAgc3RyX2MoY29sbGFwc2UgPSAiLS0tIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChjKCJvbmUiID0gIjEiLCAidHdvIiA9ICIyIiwgInRocmVlIiA9ICIzIikpDQojIFsxXSAiMSBhcHBsZS0tLTIgcGVhcnMtLS0zIGJhbmFuYXMiDQoNCiMgVXNlIGEgZnVuY3Rpb24gZm9yIG1vcmUgc29waGlzdGljYXRlZCByZXBsYWNlbWVudC4gVGhpcyBleGFtcGxlDQojIHJlcGxhY2VzIGNvbG91ciBuYW1lcyB3aXRoIHRoZWlyIGhleCB2YWx1ZXMuDQpjb2xvdXJzIDwtIHN0cl9jKCJcXGIiLCBjb2xvcnMoKSwgIlxcYiIsIGNvbGxhcHNlPSJ8IikNCmNvbDJoZXggPC0gZnVuY3Rpb24oY29sKSB7DQogIHJnYiA8LSBjb2wycmdiKGNvbCkNCiAgcmdiKHJnYlsicmVkIiwgXSwgcmdiWyJncmVlbiIsIF0sIHJnYlsiYmx1ZSIsIF0sIG1heCA9IDI1NSkNCn0NCg0KeCA8LSBjKA0KICAiUm9zZXMgYXJlIHJlZCwgdmlvbGV0cyBhcmUgYmx1ZSIsDQogICJNeSBmYXZvdXJpdGUgY29sb3VyIGlzIGdyZWVuIg0KKQ0Kc3RyX3JlcGxhY2VfYWxsKHgsIGNvbG91cnMsIGNvbDJoZXgpDQojIFsxXSAiUm9zZXMgYXJlICNGRjAwMDAsIHZpb2xldHMgYXJlICMwMDAwRkYiDQojIFsyXSAiTXkgZmF2b3VyaXRlIGNvbG91ciBpcyAjMDBGRjAwIiAgIA0KDQoNCmBgYA0KDQojIyMjIDMuMy44IHN0cl9yZXBsYWNlX25h5oqKTkHmm7/mjaLkuLpOQeWtl+espuS4sg0KDQoqKuWHveaVsOWumuS5iS1EZXNjcmlwdGlvbiAmIFVzYWdlKirvvJoNCg0KYHN0cl9yZXBsYWNlX25hKHN0cmluZywgcmVwbGFjZW1lbnQgPSAiTkEiKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgc3RyaW5nYDog5a2X56ym5Liy77yM5a2X56ym5Liy5ZCR6YeP44CCDQoqIGByZXBsYWNlbWVudGAgOiDnlKjkuo7mm7/mjaLnmoTlrZfnrKbjgIINCg0K5oqKTkHmm7/mjaLkuLrlrZfnrKbkuLJUdXJuIE5BIGludG8gIk5BIg0KDQpgYGB7ciBzdHJfcmVwbGFjZV9uYSBleDF9DQpzdHJfcmVwbGFjZV9uYShjKE5BLCdOQScsImFiYyIpLCd4JykNCiMgWzFdICJ4IiAgICJOQSIgICJhYmMiDQoNCnN0cl9yZXBsYWNlX25hKGMoTkEsICJhYmMiLCAiZGVmIikpDQojIFsxXSAiTkEiICAiYWJjIiAiZGVmIg0KYGBgDQoNCiMjIyMgMy4zLjkgc3RyX2xvY2F0Ze+8jOaJvuWIsOeahOaooeW8j+WcqOWtl+espuS4suS4reeahOS9jee9ruOAgg0KDQoqKuWHveaVsOWumuS5iS1EZXNjcmlwdGlvbiAmIFVzYWdlKirvvJoNCg0KYHN0cl9sb2NhdGUoc3RyaW5nLCBwYXR0ZXJuKWANCmBzdHJfbG9jYXRlX2FsbChzdHJpbmcsIHBhdHRlcm4pYA0KDQoqKuWPguaVsOWIl+ihqC1Bcmd1bWVudHMqKu+8mg0KDQoqIGBzdHJpbmdgOiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIINCiogYHBhdHRlcm5gOiDljLnphY3lrZfnrKbjgIINCg0KTG9jYXRlIHRoZSBwb3NpdGlvbiBvZiBwYXR0ZXJucyBpbiBhIHN0cmluZy4NCg0KYGBge3Igc3RyX2xvY2F0ZSBzdHJfbG9jYXRlX2FsbCBleDF9DQp2YWwgPC0gYygiYWJjYSIsIDEyMywgImNiYSIpDQoNCiMg5Yy56YWNYeWcqOWtl+espuS4suS4reeahOS9jee9rg0Kc3RyX2xvY2F0ZSh2YWwsICJhIikNCiMgICAgICBzdGFydCBlbmQNCiMgWzEsXSAgICAgMSAgIDENCiMgWzIsXSAgICBOQSAgTkENCiMgWzMsXSAgICAgMyAgIDMNCg0KIyDnlKjlkJHph4/ljLnphY0NCnN0cl9sb2NhdGUodmFsLCBjKCJhIiwgMTIsICJiIikpDQojICAgICAgc3RhcnQgZW5kDQojIFsxLF0gICAgIDEgICAxDQojIFsyLF0gICAgIDEgICAyDQojIFszLF0gICAgIDIgICAyDQoNCiMg5Lul5a2X56ym5LiybWF0cml45qC85byP6L+U5ZueDQpzdHJfbG9jYXRlX2FsbCh2YWwsICJhIikNCiMgW1sxXV0NCiMgICAgICBzdGFydCBlbmQNCiMgWzEsXSAgICAgMSAgIDENCiMgWzIsXSAgICAgNCAgIDQNCiMgDQojIFtbMl1dDQojICAgICAgc3RhcnQgZW5kDQojIA0KIyBbWzNdXQ0KIyAgICAgIHN0YXJ0IGVuZA0KIyBbMSxdICAgICAzICAgMw0KDQojIOWMuemFjWHmiJZi5a2X56ym77yM5Lul5a2X56ym5LiybWF0cml45qC85byP6L+U5ZueDQpzdHJfbG9jYXRlX2FsbCh2YWwsICJbYWJdIikNCiMgW1sxXV0NCiMgICAgICBzdGFydCBlbmQNCiMgWzEsXSAgICAgMSAgIDENCiMgWzIsXSAgICAgMiAgIDINCiMgWzMsXSAgICAgNCAgIDQNCiMgDQojIFtbMl1dDQojICAgICAgc3RhcnQgZW5kDQojIA0KIyBbWzNdXQ0KIyAgICAgIHN0YXJ0IGVuZA0KIyBbMSxdICAgICAyICAgMg0KIyBbMixdICAgICAzICAgMw0KDQojIE1vcmUgRXhhbXBsZXMNCmZydWl0IDwtIGMoImFwcGxlIiwgImJhbmFuYSIsICJwZWFyIiwgInBpbmVhcHBsZSIpO2ZydWl0DQojIFsxXSAiYXBwbGUiICAgICAiYmFuYW5hIiAgICAicGVhciIgICAgICAicGluZWFwcGxlIg0Kc3RyX2xvY2F0ZShmcnVpdCwgIiQiKQ0KIyAgICAgIHN0YXJ0IGVuZA0KIyBbMSxdICAgICA2ICAgNQ0KIyBbMixdICAgICA3ICAgNg0KIyBbMyxdICAgICA1ICAgNA0KIyBbNCxdICAgIDEwICAgOQ0Kc3RyX2xvY2F0ZShmcnVpdCwgImEiKQ0KIyAgICAgIHN0YXJ0IGVuZA0KIyBbMSxdICAgICAxICAgMQ0KIyBbMixdICAgICAyICAgMg0KIyBbMyxdICAgICAzICAgMw0KIyBbNCxdICAgICA1ICAgNQ0Kc3RyX2xvY2F0ZShmcnVpdCwgImUiKQ0Kc3RyX2xvY2F0ZShmcnVpdCwgYygiYSIsICJiIiwgInAiLCAicCIpKQ0KDQpzdHJfbG9jYXRlX2FsbChmcnVpdCwgImEiKQ0Kc3RyX2xvY2F0ZV9hbGwoZnJ1aXQsICJlIikNCnN0cl9sb2NhdGVfYWxsKGZydWl0LCBjKCJhIiwgImIiLCAicCIsICJwIikpDQoNCiMgRmluZCBsb2NhdGlvbiBvZiBldmVyeSBjaGFyYWN0ZXINCnN0cl9sb2NhdGVfYWxsKGZydWl0LCAiIikNCg0KYGBgDQoNCiMjIyMgMy4zLjEwIHN0cl9leHRyYWN05LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN5qih5byPDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX2V4dHJhY3Qoc3RyaW5nLCBwYXR0ZXJuKWANCmBzdHJfZXh0cmFjdF9hbGwoc3RyaW5nLCBwYXR0ZXJuLCBzaW1wbGlmeSA9IEZBTFNFKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgc3RyaW5nYDog5a2X56ym5Liy77yM5a2X56ym5Liy5ZCR6YeP44CCDQoqIGBwYXR0ZXJuYDog5Yy56YWN5a2X56ym44CCDQoqIGBzaW1wbGlmeWA6IOi/lOWbnuWAvO+8jGBUUlVFYOi/lOWbnmBtYXRyaXhg77yMYEZBTFNFYOi/lOWbnuWtl+espuS4suWQkemHjw0KDQpFeHRyYWN0IG1hdGNoaW5nIHBhdHRlcm5zIGZyb20gYSBzdHJpbmcuDQoNCmBgYHtyIHN0cl9leHRyYWN0IHN0cl9leHRyYWN0X2FsbCBleDF9DQp2YWwgPC0gYygiYWJjYTQiLCAxMjMsICJjYmEyIikNCg0KIyDov5Tlm57ljLnphY3nmoTmlbDlrZcNCnN0cl9leHRyYWN0KHZhbCwgIlxcZCIpDQojIFsxXSAiNCIgIjEiICIyIg0KDQojIOi/lOWbnuWMuemFjeeahOWtl+espg0Kc3RyX2V4dHJhY3QodmFsLCAiW2Etel0rIikNCiMgWzFdICJhYmNhIiBOQSAgICAgImNiYSIgDQoNCg0KdmFsIDwtIGMoImFiY2E0IiwgMTIzLCAiY2JhMiIpDQpzdHJfZXh0cmFjdF9hbGwodmFsLCAiXFxkIikNCiMgW1sxXV0NCiMgWzFdICI0Ig0KIyANCiMgW1syXV0NCiMgWzFdICIxIiAiMiIgIjMiDQojIA0KIyBbWzNdXQ0KIyBbMV0gIjIiDQoNCnN0cl9leHRyYWN0X2FsbCh2YWwsICJbYS16XSsiKQ0KIyBbWzFdXQ0KIyBbMV0gImFiY2EiDQojIA0KIyBbWzJdXQ0KIyBjaGFyYWN0ZXIoMCkNCiMgDQojIFtbM11dDQojIFsxXSAiY2JhIg0KDQojIE1vcmUgRXhhbXBsZXMNCnNob3BwaW5nX2xpc3QgPC0gYygiYXBwbGVzIHg0IiwgImJhZyBvZiBmbG91ciIsICJiYWcgb2Ygc3VnYXIiLCAibWlsayB4MiIpDQojIGV4dHJhY3QgZGlnaXRzDQpzdHJfZXh0cmFjdChzaG9wcGluZ19saXN0LCAiXFxkIikNCiMgWzFdICI0IiBOQSAgTkEgICIyIg0KDQojIGV4dHJhY3QgY2hhcmFjdGVyDQpzdHJfZXh0cmFjdChzaG9wcGluZ19saXN0LCAiW2Etel0rIikNCiMgWzFdICJhcHBsZXMiICJiYWciICAgICJiYWciICAgICJtaWxrIiANCg0KIyBleHRyYWN0IDQgY2hhcmFjdGVycw0Kc3RyX2V4dHJhY3Qoc2hvcHBpbmdfbGlzdCwgIlthLXpdezEsNH0iKQ0KIyBbMV0gImFwcGwiICJiYWciICAiYmFnIiAgIm1pbGsiDQoNCnN0cl9leHRyYWN0KHNob3BwaW5nX2xpc3QsICJcXGJbYS16XXsxLDR9XFxiIikNCiMgWzFdIE5BICAgICAiYmFnIiAgImJhZyIgICJtaWxrIg0KDQojIEV4dHJhY3QgYWxsIG1hdGNoZXMNCnN0cl9leHRyYWN0X2FsbChzaG9wcGluZ19saXN0LCAiW2Etel0rIikNCnN0cl9leHRyYWN0X2FsbChzaG9wcGluZ19saXN0LCAiXFxiW2Etel0rXFxiIikNCnN0cl9leHRyYWN0X2FsbChzaG9wcGluZ19saXN0LCAiXFxkIikNCg0KIyBTaW1wbGlmeSByZXN1bHRzIGludG8gY2hhcmFjdGVyIG1hdHJpeA0Kc3RyX2V4dHJhY3RfYWxsKHNob3BwaW5nX2xpc3QsICJcXGJbYS16XStcXGIiLCBzaW1wbGlmeSA9IFRSVUUpDQpzdHJfZXh0cmFjdF9hbGwoc2hvcHBpbmdfbGlzdCwgIlxcZCIsIHNpbXBsaWZ5ID0gVFJVRSkNCg0KIyBFeHRyYWN0IGFsbCB3b3Jkcw0Kc3RyX2V4dHJhY3RfYWxsKCJUaGlzIGlzLCBzdXByaXNpbmdseSwgYSBzZW50ZW5jZS4iLCBib3VuZGFyeSgid29yZCIpKQ0KDQpgYGANCg0KIyMjIDMuNCDlrZfnrKbkuLLlj5jmjaLlh73mlbANCg0KKiBgc3RyX2NvbnZgOgnlrZfnrKbnvJbnoIHovazmjaINCiogYHN0cl90b191cHBlcmA6IOWtl+espuS4sui9rOaIkOWkp+WGmQ0KKiBgc3RyX3RvX2xvd2VyYDog5a2X56ym5Liy6L2s5oiQ5bCP5YaZLOinhOWImeWQjGBzdHJfdG9fdXBwZXJgDQoqIGBzdHJfdG9fdGl0bGVgOiDlrZfnrKbkuLLovazmiJDpppblrZfmr43lpKflhpks6KeE5YiZ5ZCMYHN0cl90b191cHBlcmANCg0KIyMjIyAzLjQuMSBzdHJfY29udjrlrZfnrKbnvJbnoIHovazmjaINCg0KKirlh73mlbDlrprkuYktRGVzY3JpcHRpb24gJiBVc2FnZSoq77yaDQoNCmBzdHJfY29udihzdHJpbmcsIGVuY29kaW5nKWANCg0KKirlj4LmlbDliJfooagtQXJndW1lbnRzKirvvJoNCg0KKiBgc3RyaW5nYDog5a2X56ym5Liy77yM5a2X56ym5Liy5ZCR6YeP44CCDQoqIGBlbmNvZGluZ2A6IOe8lueggeWQjeOAgg0KDQrlr7nkuK3mlofov5vooYzovaznoIHlpITnkIbjgIJTcGVjaWZ5IHRoZSBlbmNvZGluZyBvZiBhIHN0cmluZy4NCg0KYGBge3Igc3RyX2NvbnYgZXgxfQ0KIyBFeGFtcGxlIGZyb20gZW5jb2Rpbmc/c3RyaW5naTo6c3RyaW5naQ0KeCA8LSByYXdUb0NoYXIoYXMucmF3KDE3NykpO3gNCiMgWzFdICI/DQpzdHJfY29udih4LCAiSVNPLTg4NTktMiIpICMgUG9saXNoICJhIHdpdGggb2dvbmVrIg0KIyBbMV0gIsSFIg0Kc3RyX2NvbnYoeCwgIklTTy04ODU5LTEiKSAjIFBsdXMtbWludXMNCiMgWzFdICLCsSINCg0KIyDmiorkuK3mloflrZfnrKblrZfoioLljJYNCnggPC0gY2hhclRvUmF3KCfkvaDlpb0nKTt4DQojIFsxXSBjNCBlMyBiYSBjMw0KDQojIOm7mOiupFJTdHVkaW/ns7vnu5/lrZfnrKbpm4bkuLpVVEYtOA0Kc3RyX2NvbnYoeCwgIlVURi04IikNCiMgWzFdICLkvaDlpb0iDQoNCiMg6buY6K6kd2lu57O757uf5a2X56ym6ZuG5Li6R0JL77yMR0IyMzEy5Li6R0JL5a2X6ZuG77yM6L2s56CBTk/mraPluLgNCnN0cl9jb252KHgsICJHQksiKQ0KIyBbMV0gIua1o+eKsuOCvSINCnN0cl9jb252KHgsICJHQjIzMTIiKQ0KIyBpbnB1dCBkYXRhIFx4ZmZmZmZmYTAgaW4gY3VycmVudCBzb3VyY2UgZW5jb2RpbmcgY291bGQgbm90IGJlIGNvbnZlcnRlZCB0byBVbmljb2RlaW5wdXQgZGF0YSBceGZmZmZmZmJkIGluIGN1cnJlbnQgc291cmNlIGVuY29kaW5nIGNvdWxkIG5vdCBiZSBjb252ZXJ0ZWQgdG8gVW5pY29kZVsxXSAi5rWjXDAzMua/glwwMzIiDQoNCiMg6L2sVVRGLTjlpLHotKUNCnN0cl9jb252KHgsICJVVEYtOCIpDQojIFsxXSAi77+977+977+9Ig0KIyBXYXJuaW5nIG1lc3NhZ2VzOg0KIyAxOiBJbiBzdHJpX2NvbnYoc3RyaW5nLCBlbmNvZGluZywgIlVURi04IikgOg0KIyAgIGlucHV0IGRhdGEgXHhmZmZmZmZjNCBpbiBjdXJyZW50IHNvdXJjZSBlbmNvZGluZyBjb3VsZCBub3QgYmUgY29udmVydGVkIHRvIFVuaWNvZGUNCiMgMjogSW4gc3RyaV9jb252KHN0cmluZywgZW5jb2RpbmcsICJVVEYtOCIpIDoNCiMgICBpbnB1dCBkYXRhIFx4ZmZmZmZmZTNceGZmZmZmZmJhIGluIGN1cnJlbnQgc291cmNlIGVuY29kaW5nIGNvdWxkIG5vdCBiZSBjb252ZXJ0ZWQgdG8gVW5pY29kZQ0KIyAzOiBJbiBzdHJpX2NvbnYoc3RyaW5nLCBlbmNvZGluZywgIlVURi04IikgOg0KIyAgIGlucHV0IGRhdGEgXHhmZmZmZmZjMyBpbiBjdXJyZW50IHNvdXJjZSBlbmNvZGluZyBjb3VsZCBub3QgYmUgY29udmVydGVkIHRvIFVuaWNvZGUNCmBgYA0KDQrmiop1bmljb2Rl6L2sVVRGLTgNCg0KYGBge3Igc3RyX2NvbnYgZXgyfQ0KeDEgPC0gIlx1NTMxN1x1NGVhYyINCg0Kc3RyX2NvbnYoeDEsICJVVEYtOCIpDQojIFsxXSAi5YyX5LqsIg0KYGBgDQoNCg0KIyMjIyAzLjQuMiBzdHJfdG9fdXBwZXIs5a2X56ym5Liy5aSn5YaZ6L2s5o2i44CCDQoNCioq5Ye95pWw5a6a5LmJLURlc2NyaXB0aW9uICYgVXNhZ2UqKu+8mg0KDQpgc3RyX3RvX3VwcGVyKHN0cmluZywgbG9jYWxlID0gIiIpYA0KYHN0cl90b19sb3dlcihzdHJpbmcsIGxvY2FsZSA9ICIiKWANCmBzdHJfdG9fdGl0bGUoc3RyaW5nLCBsb2NhbGUgPSAiIilgDQoNCioq5Y+C5pWw5YiX6KGoLUFyZ3VtZW50cyoq77yaDQoNCiogYHN0cmluZ2A6IOWtl+espuS4suOAgg0KKiBgbG9jYWxlYDrmjInlk6rnp43or63oqIDkuaDmg6/mjpLluo8NCg0K5a2X56ym5Liy5aSn5YaZ6L2s5o2iQ29udmVydCBjYXNlIG9mIGEgc3RyaW5nLjoNCg0KYGBge3Igc3RyX3RvX3VwcGVyIHN0cl90b19sb3dlciBzdHJfdG9fdGl0bGUgZXgxfQ0KdmFsIDwtICJJIGFtIGNvbmFuLiBXZWxjb21lIHRvIG15IGJsb2chIGh0dHA6Ly9mZW5zLm1lIg0KDQojIOWFqOWkp+WGmQ0Kc3RyX3RvX3VwcGVyKHZhbCkNCiMgWzFdICJJIEFNIENPTkFOLiBXRUxDT01FIFRPIE1ZIEJMT0chIEhUVFA6Ly9GRU5TLk1FIg0KDQojIOWFqOWwj+WGmQ0Kc3RyX3RvX2xvd2VyKHZhbCkNCiMgWzFdICJpIGFtIGNvbmFuLiB3ZWxjb21lIHRvIG15IGJsb2chIGh0dHA6Ly9mZW5zLm1lIg0KDQojIOmmluWtl+avjeWkp+WGmQ0Kc3RyX3RvX3RpdGxlKHZhbCkNCiMgWzFdICJJIEFtIENvbmFuLiBXZWxjb21lIFRvIE15IEJsb2chIEh0dHA6Ly9GZW5zLk1lIg0KDQojIE1vcmUgRXhhbXBsZXMNCmRvZyA8LSAiVGhlIHF1aWNrIGJyb3duIGRvZyI7ZG9nDQpzdHJfdG9fdXBwZXIoZG9nKQ0KIyBbMV0gIlRIRSBRVUlDSyBCUk9XTiBET0ciDQpzdHJfdG9fbG93ZXIoZG9nKQ0KIyBbMV0gInRoZSBxdWljayBicm93biBkb2ciDQpzdHJfdG9fdGl0bGUoZG9nKQ0KIyBbMV0gIlRoZSBRdWljayBCcm93biBEb2ciDQoNCiMgTG9jYWxlIG1hdHRlcnMhIA0Kc3RyX3RvX3VwcGVyKCJpIikgIyBFbmdsaXNoDQpzdHJfdG9fdXBwZXIoImkiLCAidHIiKSAjIFR1cmtpc2gNCg0KYGBgDQoNCiMjIyA0LiDlj4LmlbDmjqfliLblh73mlbAo5LuF55So5LqO5p6E6YCg5Yqf6IO955qE5Y+C5pWw77yM5LiN6IO954us56uL5L2/55SoKeOAgg0KDQoqIGBib3VuZGFyeWA6CeWumuS5ieS9v+eUqOi+ueeVjA0KKiBgY29sbGA6CeWumuS5ieWtl+espuS4suagh+WHhuaOkuW6j+inhOWImeOAgg0KKiBgZml4ZWRgOgnlrprkuYnnlKjkuo7ljLnphY3nmoTlrZfnrKbvvIzljIXmi6zmraPliJnooajovr7lvI/kuK3nmoTovazkuYnnrKYNCiogYHJlZ2V4YDoJ5a6a5LmJ5q2j5YiZ6KGo6L6+5byPDQoNCiMjIDUuIOaAu+e7kw0KDQrlrZfnrKbkuLLlnKjlubPluLjnmoTmlbDmja7lpITnkIbkuK3nu4/luLjnlKjov4fvvIzpnIDopoHlr7nlrZfnrKbkuLLov5vooYzliIblibLjgIHov57mjqXjgIHovazmjaLnrYnmk43kvZzvvIzmnKznr4fkuK3pgJrov4fku4vnu41zdHJpbmdy77yM54G15rS755qE5a2X56ym5Liy5aSE55CG5bqT77yM5Y+v5Lul5pyJ5pWI5Zyw5o+Q6auY5Luj56CB55qE57yW5YaZ5pWI546H44CC5pyJ5LqG5aW955qE5bel5YW377yM5Zyo55SoUuivreiogOWkhOeQhuWtl+espuS4suWwsemhuuaJi+S6huOAgg0KDQojIyMg6L2s6L296K+35rOo5piO5Ye65aSE77yaaHR0cDovL2Jsb2cuZmVucy5tZS9yLXN0cmluZ3IvDQoNCg0KIyMgNi4g5YW25LuW5Luj56CBDQpgYGB7ciBvdGhlciBleDF9DQoj55Sf5oiQ5pWw5o2uICANCnNldC5zZWVkKDEyMyk7ICANCm4gPSA1MDAwMDAwOyAgDQpwID0gNTsgIA0Kc3lzdGVtLnRpbWUoeCA8LSBtYXRyaXgocm5vcm0obiAqIHApLCBuLCBwKSk7ICANCnggPSBjYmluZCgxLCB4KTsgIA0KYmV0ID0gYygyLCByZXAoMSwgcCkpOyAgDQp5ID0gYyh4ICUqJSBiZXQpICsgcm5vcm0obik7ICANCiMgR2FyYmFnZSBDb2xsZWN0aW9uDQpnYygpOyAgDQpkYXQgPSBhcy5kYXRhLmZyYW1lKHgpOyAgDQpybSh4KTsgIA0KZ2MoKTsgIA0KZGF0JHkgPSB5OyAgDQpybSh5KTsgIA0KZ2MoKTsgIA0KY29sbmFtZXMoZGF0KSA9IGMocGFzdGUoIngiLCAwOnAsIHNlcCA9ICIiKSwgInkiKTsgIA0KZ2MoKTsgIA0KP2djKCkNCmBgYA0KDQo=